authenticator_ctap2_2021/ctap2/commands/
client_pin.rs

1use super::{get_info::AuthenticatorInfo, Command, CommandError, RequestCtap2, StatusCode};
2use crate::crypto::{
3    authenticate, decrypt, encapsulate, encrypt, BackendError, COSEKey, CryptoError, ECDHSecret,
4};
5use crate::transport::errors::HIDError;
6use crate::u2ftypes::U2FDevice;
7use serde::{
8    de::{Error as SerdeError, MapAccess, Visitor},
9    ser::SerializeMap,
10    Deserialize, Deserializer, Serialize, Serializer,
11};
12use serde_bytes::ByteBuf;
13use serde_cbor::de::from_slice;
14use serde_cbor::ser::to_vec;
15use serde_cbor::Value;
16use sha2::{Digest, Sha256};
17use std::error::Error as StdErrorT;
18use std::fmt;
19
20// use serde::Deserialize; cfg[test]
21
22#[derive(Debug, Copy, Clone)]
23#[repr(u8)]
24pub enum PINSubcommand {
25    GetRetries = 0x01,
26    GetKeyAgreement = 0x02,
27    SetPIN = 0x03,
28    ChangePIN = 0x04,
29    GetPINToken = 0x05,
30}
31
32#[derive(Debug)]
33pub struct ClientPIN {
34    pin_protocol: Option<u8>,
35    subcommand: PINSubcommand,
36    key_agreement: Option<COSEKey>,
37    pin_auth: Option<PinAuth>,
38    new_pin_enc: Option<ByteBuf>,
39    pin_hash_enc: Option<ByteBuf>,
40}
41
42impl Default for ClientPIN {
43    fn default() -> Self {
44        ClientPIN {
45            pin_protocol: None,
46            subcommand: PINSubcommand::GetRetries,
47            key_agreement: None,
48            pin_auth: None,
49            new_pin_enc: None,
50            pin_hash_enc: None,
51        }
52    }
53}
54
55impl Serialize for ClientPIN {
56    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
57    where
58        S: Serializer,
59    {
60        // Need to define how many elements are going to be in the map
61        // beforehand
62        let mut map_len = 1;
63        if self.pin_protocol.is_some() {
64            map_len += 1;
65        }
66        if self.key_agreement.is_some() {
67            map_len += 1;
68        }
69        if self.pin_auth.is_some() {
70            map_len += 1;
71        }
72        if self.new_pin_enc.is_some() {
73            map_len += 1;
74        }
75        if self.pin_hash_enc.is_some() {
76            map_len += 1;
77        }
78
79        let mut map = serializer.serialize_map(Some(map_len))?;
80        if let Some(ref pin_protocol) = self.pin_protocol {
81            map.serialize_entry(&1, pin_protocol)?;
82        }
83        let command: u8 = self.subcommand as u8;
84        map.serialize_entry(&2, &command)?;
85        if let Some(ref key_agreement) = self.key_agreement {
86            map.serialize_entry(&3, key_agreement)?;
87        }
88        if let Some(ref pin_auth) = self.pin_auth {
89            map.serialize_entry(&4, &ByteBuf::from(pin_auth.as_ref()))?;
90        }
91        if let Some(ref new_pin_enc) = self.new_pin_enc {
92            map.serialize_entry(&5, new_pin_enc)?;
93        }
94        if let Some(ref pin_hash_enc) = self.pin_hash_enc {
95            map.serialize_entry(&6, pin_hash_enc)?;
96        }
97
98        map.end()
99    }
100}
101
102pub trait ClientPINSubCommand {
103    type Output;
104    fn as_client_pin(&self) -> Result<ClientPIN, CommandError>;
105    fn parse_response_payload(&self, input: &[u8]) -> Result<Self::Output, CommandError>;
106}
107
108struct ClientPinResponse {
109    key_agreement: Option<COSEKey>,
110    pin_token: Option<EncryptedPinToken>,
111    /// Number of PIN attempts remaining before lockout.
112    retries: Option<u8>,
113}
114
115impl<'de> Deserialize<'de> for ClientPinResponse {
116    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117    where
118        D: Deserializer<'de>,
119    {
120        struct ClientPinResponseVisitor;
121
122        impl<'de> Visitor<'de> for ClientPinResponseVisitor {
123            type Value = ClientPinResponse;
124
125            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
126                formatter.write_str("a map")
127            }
128
129            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
130            where
131                M: MapAccess<'de>,
132            {
133                let mut key_agreement = None;
134                let mut pin_token = None;
135                let mut retries = None;
136                while let Some(key) = map.next_key()? {
137                    match key {
138                        1 => {
139                            if key_agreement.is_some() {
140                                return Err(SerdeError::duplicate_field("key_agreement"));
141                            }
142                            key_agreement = map.next_value()?;
143                        }
144                        2 => {
145                            if pin_token.is_some() {
146                                return Err(SerdeError::duplicate_field("pin_token"));
147                            }
148                            pin_token = map.next_value()?;
149                        }
150                        3 => {
151                            if retries.is_some() {
152                                return Err(SerdeError::duplicate_field("retries"));
153                            }
154                            retries = Some(map.next_value()?);
155                        }
156                        k => return Err(M::Error::custom(format!("unexpected key: {:?}", k))),
157                    }
158                }
159                Ok(ClientPinResponse {
160                    key_agreement,
161                    pin_token,
162                    retries,
163                })
164            }
165        }
166        deserializer.deserialize_bytes(ClientPinResponseVisitor)
167    }
168}
169
170#[derive(Debug)]
171pub struct GetKeyAgreement {
172    pin_protocol: u8,
173}
174
175impl GetKeyAgreement {
176    pub fn new(info: &AuthenticatorInfo) -> Result<Self, CommandError> {
177        if info.pin_protocols.contains(&1) {
178            Ok(GetKeyAgreement { pin_protocol: 1 })
179        } else {
180            Err(CommandError::UnsupportedPinProtocol)
181        }
182    }
183}
184
185impl ClientPINSubCommand for GetKeyAgreement {
186    type Output = KeyAgreement;
187
188    fn as_client_pin(&self) -> Result<ClientPIN, CommandError> {
189        Ok(ClientPIN {
190            pin_protocol: Some(self.pin_protocol),
191            subcommand: PINSubcommand::GetKeyAgreement,
192            ..ClientPIN::default()
193        })
194    }
195
196    fn parse_response_payload(&self, input: &[u8]) -> Result<Self::Output, CommandError> {
197        let value: Value = from_slice(input).map_err(CommandError::Deserializing)?;
198        debug!("GetKeyAgreement::parse_response_payload {:?}", value);
199
200        let get_pin_response: ClientPinResponse =
201            from_slice(input).map_err(CommandError::Deserializing)?;
202        if let Some(key_agreement) = get_pin_response.key_agreement {
203            Ok(KeyAgreement(key_agreement))
204        } else {
205            Err(CommandError::MissingRequiredField("key_agreement"))
206        }
207    }
208}
209
210#[derive(Debug)]
211pub struct GetPinToken<'sc, 'pin> {
212    pin_protocol: u8,
213    shared_secret: &'sc ECDHSecret,
214    pin: &'pin Pin,
215}
216
217impl<'sc, 'pin> GetPinToken<'sc, 'pin> {
218    pub fn new(
219        info: &AuthenticatorInfo,
220        shared_secret: &'sc ECDHSecret,
221        pin: &'pin Pin,
222    ) -> Result<Self, CommandError> {
223        if info.pin_protocols.contains(&1) {
224            Ok(GetPinToken {
225                pin_protocol: 1,
226                shared_secret,
227                pin,
228            })
229        } else {
230            Err(CommandError::UnsupportedPinProtocol)
231        }
232    }
233}
234
235impl<'sc, 'pin> ClientPINSubCommand for GetPinToken<'sc, 'pin> {
236    type Output = PinToken;
237
238    fn as_client_pin(&self) -> Result<ClientPIN, CommandError> {
239        let input = self.pin.for_pin_token();
240        trace!("pin_hash = {:#04X?}", &input.as_ref());
241        let pin_hash_enc = encrypt(self.shared_secret.shared_secret(), input.as_ref())
242            .map_err(|e| CryptoError::Backend(e))?;
243        trace!("pin_hash_enc = {:#04X?}", &pin_hash_enc);
244
245        Ok(ClientPIN {
246            pin_protocol: Some(self.pin_protocol),
247            subcommand: PINSubcommand::GetPINToken,
248            key_agreement: Some(self.shared_secret.my_public_key().clone()),
249            pin_hash_enc: Some(ByteBuf::from(pin_hash_enc)),
250            ..ClientPIN::default()
251        })
252    }
253
254    fn parse_response_payload(&self, input: &[u8]) -> Result<Self::Output, CommandError> {
255        let value: Value = from_slice(input).map_err(CommandError::Deserializing)?;
256        debug!("GetKeyAgreement::parse_response_payload {:?}", value);
257
258        let get_pin_response: ClientPinResponse =
259            from_slice(input).map_err(CommandError::Deserializing)?;
260        match get_pin_response.pin_token {
261            Some(encrypted_pin_token) => {
262                let pin_token = decrypt(
263                    self.shared_secret.shared_secret(),
264                    encrypted_pin_token.as_ref(),
265                )
266                .map_err(|e| CryptoError::Backend(e))?;
267                let pin_token = PinToken(pin_token);
268                Ok(pin_token)
269            }
270            None => Err(CommandError::MissingRequiredField("key_agreement")),
271        }
272    }
273}
274
275#[derive(Debug)]
276pub struct GetRetries {}
277
278impl GetRetries {
279    pub fn new() -> Self {
280        GetRetries {}
281    }
282}
283
284impl ClientPINSubCommand for GetRetries {
285    type Output = u8;
286
287    fn as_client_pin(&self) -> Result<ClientPIN, CommandError> {
288        Ok(ClientPIN {
289            subcommand: PINSubcommand::GetRetries,
290            ..ClientPIN::default()
291        })
292    }
293
294    fn parse_response_payload(&self, input: &[u8]) -> Result<Self::Output, CommandError> {
295        let value: Value = from_slice(input).map_err(CommandError::Deserializing)?;
296        debug!("GetKeyAgreement::parse_response_payload {:?}", value);
297
298        let get_pin_response: ClientPinResponse =
299            from_slice(input).map_err(CommandError::Deserializing)?;
300        match get_pin_response.retries {
301            Some(retries) => Ok(retries),
302            None => Err(CommandError::MissingRequiredField("retries")),
303        }
304    }
305}
306
307#[derive(Debug)]
308pub struct SetNewPin<'sc, 'pin> {
309    pin_protocol: u8,
310    shared_secret: &'sc ECDHSecret,
311    new_pin: &'pin Pin,
312}
313
314impl<'sc, 'pin> SetNewPin<'sc, 'pin> {
315    pub fn new(
316        info: &AuthenticatorInfo,
317        shared_secret: &'sc ECDHSecret,
318        new_pin: &'pin Pin,
319    ) -> Result<Self, CommandError> {
320        if info.pin_protocols.contains(&1) {
321            Ok(SetNewPin {
322                pin_protocol: 1,
323                shared_secret,
324                new_pin,
325            })
326        } else {
327            Err(CommandError::UnsupportedPinProtocol)
328        }
329    }
330}
331
332impl<'sc, 'pin> ClientPINSubCommand for SetNewPin<'sc, 'pin> {
333    type Output = ();
334
335    fn as_client_pin(&self) -> Result<ClientPIN, CommandError> {
336        if self.new_pin.as_bytes().len() > 63 {
337            return Err(CommandError::StatusCode(
338                StatusCode::PinPolicyViolation,
339                None,
340            ));
341        }
342        // Padding the PIN with trailing zeros, according to spec
343        let input: Vec<u8> = self
344            .new_pin
345            .as_bytes()
346            .iter()
347            .chain(std::iter::repeat(&0x00))
348            .take(64)
349            .cloned()
350            .collect();
351
352        let shared_secret = self.shared_secret.shared_secret();
353        // AES256-CBC(sharedSecret, IV=0, newPin)
354        let new_pin_enc =
355            encrypt(shared_secret, input.as_ref()).map_err(|e| CryptoError::Backend(e))?;
356
357        // LEFT(HMAC-SHA-265(sharedSecret, newPinEnc), 16)
358        let pin_auth = PinToken(shared_secret.to_vec())
359            .auth(&new_pin_enc)
360            .map_err(CommandError::Crypto)?;
361
362        Ok(ClientPIN {
363            pin_protocol: Some(self.pin_protocol),
364            subcommand: PINSubcommand::SetPIN,
365            key_agreement: Some(self.shared_secret.my_public_key().clone()),
366            new_pin_enc: Some(ByteBuf::from(new_pin_enc)),
367            pin_auth: Some(pin_auth),
368            ..ClientPIN::default()
369        })
370    }
371
372    fn parse_response_payload(&self, input: &[u8]) -> Result<Self::Output, CommandError> {
373        // Should be an empty response or a valid cbor-value (which we ignore)
374        if input.is_empty() {
375            Ok(())
376        } else {
377            let _: Value = from_slice(input).map_err(CommandError::Deserializing)?;
378            Ok(())
379        }
380    }
381}
382
383#[derive(Debug)]
384pub struct ChangeExistingPin<'sc, 'pin> {
385    pin_protocol: u8,
386    shared_secret: &'sc ECDHSecret,
387    current_pin: &'pin Pin,
388    new_pin: &'pin Pin,
389}
390
391impl<'sc, 'pin> ChangeExistingPin<'sc, 'pin> {
392    pub fn new(
393        info: &AuthenticatorInfo,
394        shared_secret: &'sc ECDHSecret,
395        current_pin: &'pin Pin,
396        new_pin: &'pin Pin,
397    ) -> Result<Self, CommandError> {
398        if info.pin_protocols.contains(&1) {
399            Ok(ChangeExistingPin {
400                pin_protocol: 1,
401                shared_secret,
402                current_pin,
403                new_pin,
404            })
405        } else {
406            Err(CommandError::UnsupportedPinProtocol)
407        }
408    }
409}
410
411impl<'sc, 'pin> ClientPINSubCommand for ChangeExistingPin<'sc, 'pin> {
412    type Output = ();
413
414    fn as_client_pin(&self) -> Result<ClientPIN, CommandError> {
415        if self.new_pin.as_bytes().len() > 63 {
416            return Err(CommandError::StatusCode(
417                StatusCode::PinPolicyViolation,
418                None,
419            ));
420        }
421        // Padding the PIN with trailing zeros, according to spec
422        let input: Vec<u8> = self
423            .new_pin
424            .as_bytes()
425            .iter()
426            .chain(std::iter::repeat(&0x00))
427            .take(64)
428            .cloned()
429            .collect();
430
431        let shared_secret = self.shared_secret.shared_secret();
432        // AES256-CBC(sharedSecret, IV=0, newPin)
433        let new_pin_enc =
434            encrypt(shared_secret, input.as_ref()).map_err(|e| CryptoError::Backend(e))?;
435
436        // AES256-CBC(sharedSecret, IV=0, LEFT(SHA-256(oldPin), 16))
437        let input = self.current_pin.for_pin_token();
438        let pin_hash_enc = encrypt(self.shared_secret.shared_secret(), input.as_ref())
439            .map_err(|e| CryptoError::Backend(e))?;
440
441        // LEFT(HMAC-SHA-265(sharedSecret, newPinEnc), 16)
442        let pin_auth = PinToken(shared_secret.to_vec())
443            .auth(&[new_pin_enc.as_slice(), pin_hash_enc.as_slice()].concat())
444            .map_err(CommandError::Crypto)?;
445
446        Ok(ClientPIN {
447            pin_protocol: Some(self.pin_protocol),
448            subcommand: PINSubcommand::ChangePIN,
449            key_agreement: Some(self.shared_secret.my_public_key().clone()),
450            new_pin_enc: Some(ByteBuf::from(new_pin_enc)),
451            pin_hash_enc: Some(ByteBuf::from(pin_hash_enc)),
452            pin_auth: Some(pin_auth),
453        })
454    }
455
456    fn parse_response_payload(&self, input: &[u8]) -> Result<Self::Output, CommandError> {
457        // Should be an empty response or a valid cbor-value (which we ignore)
458        if input.is_empty() {
459            Ok(())
460        } else {
461            let _: Value = from_slice(input).map_err(CommandError::Deserializing)?;
462            Ok(())
463        }
464    }
465}
466
467impl<T> RequestCtap2 for T
468where
469    T: ClientPINSubCommand,
470    T: fmt::Debug,
471{
472    type Output = <T as ClientPINSubCommand>::Output;
473
474    fn command() -> Command {
475        Command::ClientPin
476    }
477
478    fn wire_format<Dev>(&self, _dev: &mut Dev) -> Result<Vec<u8>, HIDError>
479    where
480        Dev: U2FDevice,
481    {
482        let client_pin = self.as_client_pin()?;
483        let output = to_vec(&client_pin).map_err(CommandError::Serializing)?;
484        trace!("client subcommmand: {:04X?}", &output);
485
486        Ok(output)
487    }
488
489    fn handle_response_ctap2<Dev>(
490        &self,
491        _dev: &mut Dev,
492        input: &[u8],
493    ) -> Result<Self::Output, HIDError>
494    where
495        Dev: U2FDevice,
496    {
497        trace!("Client pin subcomand response:{:04X?}", &input);
498        if input.is_empty() {
499            return Err(CommandError::InputTooSmall.into());
500        }
501
502        let status: StatusCode = input[0].into();
503        debug!("response status code: {:?}", status);
504        if status.is_ok() {
505            let res = <T as ClientPINSubCommand>::parse_response_payload(self, &input[1..])
506                .map_err(HIDError::Command);
507            res
508        } else {
509            let add_data = if input.len() > 1 {
510                let data: Value = from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
511                Some(data)
512            } else {
513                None
514            };
515            Err(CommandError::StatusCode(status, add_data).into())
516        }
517    }
518}
519
520#[derive(Debug)]
521pub struct KeyAgreement(COSEKey);
522
523impl KeyAgreement {
524    pub fn shared_secret(&self) -> Result<ECDHSecret, CommandError> {
525        encapsulate(&self.0).map_err(|e| CommandError::Crypto(CryptoError::Backend(e)))
526    }
527}
528
529#[derive(Debug, Deserialize)]
530pub struct EncryptedPinToken(ByteBuf);
531
532impl AsRef<[u8]> for EncryptedPinToken {
533    fn as_ref(&self) -> &[u8] {
534        self.0.as_ref()
535    }
536}
537
538#[derive(Debug)]
539pub struct PinToken(Vec<u8>);
540
541impl PinToken {
542    pub fn auth(&self, payload: &[u8]) -> Result<PinAuth, CryptoError> {
543        let hmac = authenticate(self.as_ref(), payload)?;
544
545        let mut out = [0u8; 16];
546        out.copy_from_slice(&hmac[0..16]);
547
548        Ok(PinAuth(out.to_vec()))
549    }
550}
551
552impl AsRef<[u8]> for PinToken {
553    fn as_ref(&self) -> &[u8] {
554        self.0.as_ref()
555    }
556}
557
558#[derive(Debug, Clone)]
559#[cfg_attr(test, derive(Deserialize))]
560pub struct PinAuth(Vec<u8>);
561
562impl PinAuth {
563    pub(crate) fn empty_pin_auth() -> Self {
564        PinAuth(vec![])
565    }
566}
567
568impl AsRef<[u8]> for PinAuth {
569    fn as_ref(&self) -> &[u8] {
570        &self.0
571    }
572}
573
574impl Serialize for PinAuth {
575    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
576    where
577        S: Serializer,
578    {
579        serde_bytes::serialize(&self.0[..], serializer)
580    }
581}
582
583#[derive(Clone)]
584pub struct Pin(String);
585
586impl fmt::Debug for Pin {
587    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
588        write!(f, "Pin(redacted)")
589    }
590}
591
592impl Pin {
593    pub fn new(value: &str) -> Pin {
594        Pin(String::from(value))
595    }
596
597    pub fn for_pin_token(&self) -> PinAuth {
598        let mut hasher = Sha256::new();
599        hasher.update(&self.0.as_bytes());
600
601        let mut output = [0u8; 16];
602        let len = output.len();
603        output.copy_from_slice(&hasher.finalize().as_slice()[..len]);
604
605        PinAuth(output.to_vec())
606    }
607
608    pub fn as_bytes(&self) -> &[u8] {
609        self.0.as_bytes()
610    }
611}
612
613#[derive(Clone, Debug, Serialize)]
614pub enum PinError {
615    PinRequired,
616    PinIsTooShort,
617    PinIsTooLong(usize),
618    InvalidKeyLen,
619    InvalidPin(Option<u8>),
620    PinAuthBlocked,
621    PinBlocked,
622    Backend(BackendError),
623}
624
625impl fmt::Display for PinError {
626    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
627        match *self {
628            PinError::PinRequired => write!(f, "PinError: Pin required."),
629            PinError::PinIsTooShort => write!(f, "PinError: pin is too short"),
630            PinError::PinIsTooLong(len) => write!(f, "PinError: pin is too long ({})", len),
631            PinError::InvalidKeyLen => write!(f, "PinError: invalid key len"),
632            PinError::InvalidPin(ref e) => {
633                let mut res = write!(f, "PinError: Invalid Pin.");
634                if let Some(retries) = e {
635                    res = write!(f, " Retries left: {:?}", retries)
636                }
637                res
638            }
639            PinError::PinAuthBlocked => write!(
640                f,
641                "PinError: Pin authentication blocked. Device needs power cycle."
642            ),
643            PinError::PinBlocked => write!(
644                f,
645                "PinError: No retries left. Pin blocked. Device needs reset."
646            ),
647            PinError::Backend(ref e) => write!(f, "PinError: Crypto backend error: {:?}", e),
648        }
649    }
650}
651
652impl StdErrorT for PinError {}
653
654impl From<BackendError> for PinError {
655    fn from(e: BackendError) -> Self {
656        PinError::Backend(e)
657    }
658}