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
use crate::util::*;
use crate::messages::*;
use crate::register::*;
use crate::authorization::*;

use base64::{encode_config, decode_config, URL_SAFE_NO_PAD};
use chrono::prelude::*;
use time::Duration;
use crate::u2ferror::U2fError;

type Result<T> = ::std::result::Result<T, U2fError>;

#[derive(Clone)]
pub struct U2f {
    app_id: String,
}

#[derive(Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Challenge {
    pub app_id: String,
    pub challenge: String,
    pub timestamp: String,
}

impl Challenge {
    pub fn new() -> Self {
        Challenge {
            app_id: String::new(),
            challenge: String::new(),
            timestamp: String::new()
        }
    }
}

impl U2f {
    // The app ID is a string used to uniquely identify an U2F app
    pub fn new(app_id: String) -> Self {
        U2f {
            app_id: app_id,
        }
    }

    pub fn generate_challenge(&self) -> Result<Challenge> {
        let utc: DateTime<Utc> = Utc::now();

        let challenge_bytes = generate_challenge(32)?; 
        let challenge = Challenge {
            challenge : encode_config(&challenge_bytes, URL_SAFE_NO_PAD),
            timestamp : format!("{:?}", utc),
            app_id : self.app_id.clone()
        };
        
        Ok(challenge.clone())
    }

    pub fn request(&self, challenge: Challenge, registrations: Vec<Registration>) -> Result<U2fRegisterRequest> {
        let u2f_request = U2fRegisterRequest {
            app_id : self.app_id.clone(),
            register_requests: self.register_request(challenge),
            registered_keys: self.registered_keys(registrations)
        };

        Ok(u2f_request)
    }

    fn register_request(&self, challenge: Challenge) -> Vec<RegisterRequest> {
        let mut requests: Vec<RegisterRequest> = vec![];

        let request = RegisterRequest {
            version : U2F_V2.into(),
            challenge: challenge.challenge
        };
        requests.push(request);

        requests
    }

    pub fn register_response(&self, challenge: Challenge, response: RegisterResponse) -> Result<Registration> {
        if expiration(challenge.timestamp) > Duration::seconds(300) {
            return Err(U2fError::ChallengeExpired);
        }

        let registration_data: Vec<u8> = decode_config(&response.registration_data[..], URL_SAFE_NO_PAD).unwrap();
        let client_data: Vec<u8> = decode_config(&response.client_data[..], URL_SAFE_NO_PAD).unwrap();

        parse_registration(challenge.app_id, client_data, registration_data)
    }

    fn registered_keys(&self, registrations: Vec<Registration>) -> Vec<RegisteredKey> {
        let mut keys: Vec<RegisteredKey> = vec![];

        for registration in registrations {
            keys.push(get_registered_key(self.app_id.clone(), registration.key_handle));
        }

        keys
    }
    
    pub fn sign_request(&self, challenge: Challenge, registrations: Vec<Registration>) -> U2fSignRequest {
        let mut keys: Vec<RegisteredKey> = vec![];

        for registration in registrations {
            keys.push(get_registered_key(self.app_id.clone(), registration.key_handle));
        }

        let signed_request = U2fSignRequest {
            app_id : self.app_id.clone(),
            challenge: encode_config(challenge.challenge.as_bytes(), URL_SAFE_NO_PAD),
            registered_keys: keys
        };

        signed_request
    }  

    pub fn sign_response(&self, challenge: Challenge, reg: Registration, sign_resp: SignResponse, counter: u32) -> Result<u32> {
        if expiration(challenge.timestamp) > Duration::seconds(300) {
            return Err(U2fError::ChallengeExpired);
        }

        if sign_resp.key_handle != get_encoded(&reg.key_handle[..]) {            
            return Err(U2fError::WrongKeyHandler);
        }

        let client_data: Vec<u8> = decode_config(&sign_resp.client_data[..], URL_SAFE_NO_PAD).map_err(|_e| U2fError::InvalidClientData)?;
        let sign_data: Vec<u8> = decode_config(&sign_resp.signature_data[..], URL_SAFE_NO_PAD).map_err(|_e| U2fError::InvalidSignatureData)?;

        let public_key = reg.pub_key;

        let auth = parse_sign_response(self.app_id.clone(), client_data.clone(), public_key, sign_data.clone());

        match auth {
            Ok(ref res) => {
                // CounterTooLow is raised when the counter value received from the device is
                // lower than last stored counter value.
                if res.counter < counter {
                    return Err(U2fError::CounterTooLow);
                }
                else {
                    return Ok(res.counter);
                }
            },
                Err(e) => return Err(e),
            }
        }       
}