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
// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT

use std::convert;

use crate::error::{Error, LibraryError};

/// The configuration for a Nitrokey.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Config {
    /// If set, the stick will generate a code from the HOTP slot with the given number if numlock
    /// is pressed.  The slot number must be 0, 1 or 2.
    pub numlock: Option<u8>,
    /// If set, the stick will generate a code from the HOTP slot with the given number if capslock
    /// is pressed.  The slot number must be 0, 1 or 2.
    pub capslock: Option<u8>,
    /// If set, the stick will generate a code from the HOTP slot with the given number if
    /// scrollock is pressed.  The slot number must be 0, 1 or 2.
    pub scrollock: Option<u8>,
    /// If set, OTP generation using [`get_hotp_code`][] or [`get_totp_code`][] requires user
    /// authentication.  Otherwise, OTPs can be generated without authentication.
    ///
    /// [`get_hotp_code`]: trait.ProvideOtp.html#method.get_hotp_code
    /// [`get_totp_code`]: trait.ProvideOtp.html#method.get_totp_code
    pub user_password: bool,
}

#[derive(Debug)]
pub struct RawConfig {
    pub numlock: u8,
    pub capslock: u8,
    pub scrollock: u8,
    pub user_password: bool,
}

fn config_otp_slot_to_option(value: u8) -> Option<u8> {
    if value < 3 {
        Some(value)
    } else {
        None
    }
}

fn option_to_config_otp_slot(value: Option<u8>) -> Result<u8, Error> {
    if let Some(value) = value {
        if value < 3 {
            Ok(value)
        } else {
            Err(LibraryError::InvalidSlot.into())
        }
    } else {
        Ok(255)
    }
}

impl Config {
    /// Constructs a new instance of this struct.
    pub fn new(
        numlock: Option<u8>,
        capslock: Option<u8>,
        scrollock: Option<u8>,
        user_password: bool,
    ) -> Config {
        Config {
            numlock,
            capslock,
            scrollock,
            user_password,
        }
    }
}

impl convert::TryFrom<Config> for RawConfig {
    type Error = Error;

    fn try_from(config: Config) -> Result<RawConfig, Error> {
        Ok(RawConfig {
            numlock: option_to_config_otp_slot(config.numlock)?,
            capslock: option_to_config_otp_slot(config.capslock)?,
            scrollock: option_to_config_otp_slot(config.scrollock)?,
            user_password: config.user_password,
        })
    }
}

impl From<[u8; 5]> for RawConfig {
    fn from(data: [u8; 5]) -> Self {
        RawConfig {
            numlock: data[0],
            capslock: data[1],
            scrollock: data[2],
            user_password: data[3] != 0,
        }
    }
}

impl Into<Config> for RawConfig {
    fn into(self) -> Config {
        Config {
            numlock: config_otp_slot_to_option(self.numlock),
            capslock: config_otp_slot_to_option(self.capslock),
            scrollock: config_otp_slot_to_option(self.scrollock),
            user_password: self.user_password,
        }
    }
}