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
mod c2rust;
mod rust2c;
use std::convert::TryFrom;

include!("./types.rs");

// Unfortunately cryptoauthlib takes ATCAIfaceCfg pointer as a field inside
// _gDevice->mIface->mIfaceCFG, so it _must_ be taken from heap.bool
// It adds several lines of code to implement it...
static mut GLOBAL_IFACE_CFG_PTR: *mut cryptoauthlib_sys::ATCAIfaceCfg =
    0 as *mut cryptoauthlib_sys::ATCAIfaceCfg;

/// Creates a global ATCADevice object used by Basic API.
pub fn atcab_init(r_iface_cfg: AtcaIfaceCfg) -> AtcaStatus {
    let mut iface_cfg_ptr;
    let allow_allocation: bool =
        unsafe { GLOBAL_IFACE_CFG_PTR == std::ptr::null_mut::<cryptoauthlib_sys::ATCAIfaceCfg>() };
    if allow_allocation {
        iface_cfg_ptr = Box::new(match rust2c::r2c_atca_iface_cfg(r_iface_cfg) {
            Some(x) => x,
            None => return AtcaStatus::AtcaBadParam,
        });
        unsafe { GLOBAL_IFACE_CFG_PTR = &mut *iface_cfg_ptr };
        std::mem::forget(iface_cfg_ptr);
    }

    c2rust::c2r_enum_status(unsafe { cryptoauthlib_sys::atcab_init(GLOBAL_IFACE_CFG_PTR) })
}

/// Use the SHA command to compute a SHA-256 digest.
pub fn atcab_sha(message: Vec<u8>, digest: &mut Vec<u8>) -> AtcaStatus {
    let length: u16 = match u16::try_from(message.len()) {
        Ok(val) => val,
        Err(_) => return AtcaStatus::AtcaBadParam,
    };

    let digest_size: usize = cryptoauthlib_sys::ATCA_SHA2_256_DIGEST_SIZE as usize;

    if digest.len() != digest_size {
        digest.resize(digest_size, 0);
    }

    c2rust::c2r_enum_status(unsafe {
        cryptoauthlib_sys::atcab_sha(length, message.as_ptr(), digest.as_mut_ptr())
    })
}

/// Get the global device object
pub fn atcab_get_device() -> AtcaDevice {
    AtcaDevice {
        dev: unsafe { cryptoauthlib_sys::atcab_get_device() },
    }
}

pub fn atcab_random(rand_out: &mut Vec<u8>) -> AtcaStatus {
    if rand_out.len() != ACTA_RANDOM_BUFFER_SIZE {
        rand_out.resize(ACTA_RANDOM_BUFFER_SIZE, 0);
    }

    c2rust::c2r_enum_status(unsafe { cryptoauthlib_sys::atcab_random(rand_out.as_mut_ptr()) })
}

pub fn atcab_release() -> AtcaStatus {
    c2rust::c2r_enum_status(unsafe { cryptoauthlib_sys::atcab_release() })
}

pub fn atca_iface_setup_i2c(
    device_type: String,
    wake_delay: u16,
    rx_retries: i32,
    // I2C salve address
    slave_address: Option<u8>,
    // I2C bus number
    bus: Option<u8>,
    // I2C baud rate
    baud: Option<u32>,
) -> Result<AtcaIfaceCfg, String> {
    let atca_iface_cfg = AtcaIfaceCfg {
        iface_type: AtcaIfaceType::AtcaI2cIface,
        devtype: match device_type.as_str() {
            "atecc608a" => AtcaDeviceType::ATECC608A,
            "atecc508a" => AtcaDeviceType::ATECC508A,
            _ => {
                let e = "Unsupported device type ".to_owned() + device_type.as_str();
                return Err(e);
            }
        },
        iface: AtcaIface {
            atcai2c: AtcaIfaceI2c {
                // unwrap_or_else_return()?
                slave_address: match slave_address {
                    Some(x) => x,
                    _ => return Err("missing i2c slave address".to_owned()),
                },
                bus: match bus {
                    Some(x) => x,
                    _ => return Err("missing i2c bus".to_owned()),
                },
                baud: match baud {
                    Some(x) => x,
                    _ => return Err("missing i2c baud rate".to_owned()),
                },
            },
        },
        rx_retries,
        wake_delay,
    };
    Ok(atca_iface_cfg)
}

#[cfg(test)]
mod tests {
    use serde::Deserialize;
    use serial_test::serial;
    use std::fs::read_to_string;
    use std::path::Path;

    #[derive(Deserialize)]
    struct Config {
        pub device: Device,
        pub interface: Interface,
    }

    #[derive(Deserialize)]
    struct Device {
        pub device_type: String,
        pub iface_type: String,
        pub wake_delay: u16,
        pub rx_retries: i32,
    }

    #[derive(Deserialize)]
    struct Interface {
        pub slave_address: u8,
        pub bus: u8,
        pub baud: u32,
    }

    #[allow(dead_code)]
    fn atca_iface_setup() -> Result<super::AtcaIfaceCfg, String> {
        let config_path = Path::new("config.toml");
        let config_string = read_to_string(config_path).expect("file not found");
        let config: Config = toml::from_str(&config_string).unwrap();
        match config.device.iface_type.as_str() {
            "i2c" => super::atca_iface_setup_i2c(
                config.device.device_type,
                config.device.wake_delay,
                config.device.rx_retries,
                Some(config.interface.slave_address),
                Some(config.interface.bus),
                Some(config.interface.baud),
            ),
            _ => Err("unsupported interface type".to_owned()),
        }
    }
    #[test]
    #[serial]
    fn atcab_init() {
        let atca_iface_cfg = atca_iface_setup();
        match atca_iface_cfg {
            Ok(x) => {
                assert_eq!(x.iface_type.to_string(), "AtcaI2cIface");
                assert_eq!(super::atcab_init(x).to_string(), "AtcaSuccess");
            }
            Err(e) => {
                panic!("Error reading config.toml file: {}", e);
            }
        };
        assert_eq!(super::atcab_release().to_string(), "AtcaSuccess");
    }
    #[test]
    #[serial]
    fn atcab_sha() {
        let atca_iface_cfg = atca_iface_setup();
        let mut digest: Vec<u8> = Vec::with_capacity(64);
        assert_eq!(atca_iface_cfg.is_ok(), true);
        assert_eq!(
            super::atcab_init(atca_iface_cfg.unwrap()).to_string(),
            "AtcaSuccess"
        );

        let test_message = "TestMessage";
        let message = test_message.as_bytes().to_vec();

        assert_eq!(
            super::atcab_sha(message, &mut digest).to_string(),
            "AtcaSuccess"
        );
        assert_eq!(super::atcab_release().to_string(), "AtcaSuccess");
    }
    #[test]
    #[serial]
    fn atcab_random() {
        let atca_iface_cfg = atca_iface_setup();
        let mut rand_out = Vec::with_capacity(32);
        assert_eq!(atca_iface_cfg.is_ok(), true);
        assert_eq!(
            super::atcab_init(atca_iface_cfg.unwrap()).to_string(),
            "AtcaSuccess"
        );
        assert_eq!(
            super::atcab_random(&mut rand_out).to_string(),
            "AtcaSuccess"
        );
        assert_eq!(super::atcab_release().to_string(), "AtcaSuccess");
    }
}