Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS compatibility #4

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions commoncrypto-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "commoncrypto-sys"
version = "0.2.0"
version = "0.2.1"
authors = ["Mark Lee"]
description = "FFI bindings to Mac OS X's CommonCrypto library"
documentation = "https://docs.rs/commoncrypto-sys"
Expand All @@ -14,8 +14,6 @@ travis-ci = { repository = "malept/rust-commoncrypto" }
lint = ["clippy"]

[dependencies]
libc = "0.2"

clippy = { version = "0.0", optional = true }

[dev-dependencies]
Expand Down
15 changes: 11 additions & 4 deletions commoncrypto-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@

#![warn(missing_docs)]

extern crate libc;

use libc::{c_int, c_uint};
use std::os::raw::{c_int, c_uint};

/// Total number of operations.
const MD5_CBLOCK: usize = 64;
Expand Down Expand Up @@ -85,7 +83,7 @@ macro_rules! cc_sha2_struct {
hash: [$ty; 8],
wbuf: [$ty; 16],
}
}
};
}

cc_sha2_struct!(CC_SHA256_CTX, u32);
Expand All @@ -97,18 +95,24 @@ cc_sha2_struct!(CC_SHA512_CTX, u64);
pub enum CCDigestAlgorithm {
/// No digest algorithm
kCCDigestNone = 0,
#[cfg(target_os = "macos")]
/// MD2
kCCDigestMD2 = 1,
#[cfg(target_os = "macos")]
/// MD4
kCCDigestMD4 = 2,
/// MD5
kCCDigestMD5 = 3,
#[cfg(target_os = "macos")]
/// RIPEMD-128
kCCDigestRMD128 = 4,
#[cfg(target_os = "macos")]
/// RIPEMD-160
kCCDigestRMD160 = 5,
#[cfg(target_os = "macos")]
/// RIPEMD-256
kCCDigestRMD256 = 6,
#[cfg(target_os = "macos")]
/// RIPEMD-320
kCCDigestRMD320 = 7,
/// SHA1
Expand Down Expand Up @@ -199,6 +203,9 @@ extern "C" {
pub fn CC_SHA512_Update(ctx: *mut CC_SHA512_CTX, data: *const u8, n: usize) -> c_int;
/// Generates SHA512 hash. See `man 3cc CC_SHA` for details.
pub fn CC_SHA512_Final(md: *mut u8, ctx: *mut CC_SHA512_CTX) -> c_int;
}
#[cfg(target_os = "macos")]
extern "C" {
/// Generic digest hasher.
pub fn CCDigest(
algorithm: CCDigestAlgorithm,
Expand Down
5 changes: 3 additions & 2 deletions commoncrypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "commoncrypto"
version = "0.2.0"
version = "0.2.1"
authors = ["Mark Lee"]
description = "Idiomatic Rust wrappers for Mac OS X's CommonCrypto library"
documentation = "https://docs.rs/commoncrypto"
Expand All @@ -11,11 +11,12 @@ license = "MIT"
travis-ci = { repository = "malept/rust-commoncrypto" }

[features]
# default = ["ios-compat"]
ios-compat = []
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to be behind a feature? Shouldn't it just be gated on the OS?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So tests can be run on macOS.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced that's the right way to do it, but I'll do some research when I have some free time.

lint = ["clippy"]

[dependencies]
commoncrypto-sys = { version = "0.2.0", path = "../commoncrypto-sys" }

clippy = { version = "0.0", optional = true }

[dev-dependencies]
Expand Down
16 changes: 10 additions & 6 deletions commoncrypto/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,23 @@

//! Idiomatic Rust wrapper for `CommonCrypto`'s `CCDigestCtx` struct.

use commoncrypto_sys::{CCDigestCreate, CCDigestCtx, CCDigestDestroy, CCDigestFinal,
CCDigestGetOutputSizeFromRef, CCDigestReset, CCDigestUpdate};
use commoncrypto_sys::{
CCDigestCreate, CCDigestCtx, CCDigestDestroy, CCDigestFinal, CCDigestGetOutputSizeFromRef,
CCDigestReset, CCDigestUpdate,
};
use std::io;

pub use commoncrypto_sys::CCDigestAlgorithm;

const MAX_DIGEST_SIZE: usize = 64;

macro_rules! err_from_ccdigest_retval{
macro_rules! err_from_ccdigest_retval {
($func_name: expr, $val: expr) => {
Err(io::Error::new(io::ErrorKind::Other,
format!("{} returned nonzero: {}", $func_name, $val)))
}
Err(io::Error::new(
io::ErrorKind::Other,
format!("{} returned nonzero: {}", $func_name, $val),
))
};
}

#[derive(PartialEq, Copy, Clone, Debug)]
Expand Down
218 changes: 218 additions & 0 deletions commoncrypto/src/ios.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Copyright (c) 2016 Mark Lee
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Idiomatic Rust wrapper for `CommonCrypto`'s `CCDigestCtx` struct.

use commoncrypto_sys::*;
use std::io;
use std::os::raw::{c_int, c_void};

pub use commoncrypto_sys::CCDigestAlgorithm;

const MAX_DIGEST_SIZE: usize = 64;

macro_rules! err_from_ccdigest_retval {
($func_name: expr, $val: expr) => {
Err(io::Error::new(
io::ErrorKind::Other,
format!("{} returned nonzero: {}", $func_name, $val),
))
};
}

#[derive(PartialEq, Copy, Clone, Debug)]
enum State {
Reset,
Updated,
Finalized,
}

#[derive(Debug)]
enum AlgoData {
None,
MD5(CC_MD5_CTX),
SHA(CC_SHA512_CTX),
}

impl AlgoData {
fn as_ptr(&mut self) -> *mut c_void {
match self {
AlgoData::None => std::ptr::null_mut(),
AlgoData::MD5(ref mut md5) => md5 as *mut CC_MD5_CTX as *mut c_void,
AlgoData::SHA(ref mut sha) => sha as *mut CC_SHA512_CTX as *mut c_void,
}
}
}

/// Generates cryptographic hashes.
#[derive(Debug)]
pub struct Hasher {
state: State,
ctx: AlgoData,
init_f: unsafe extern "C" fn(*mut c_void) -> c_int,
update_f: unsafe extern "C" fn(*mut c_void, *const u8, usize) -> c_int,
final_f: unsafe extern "C" fn(*mut u8, *mut c_void) -> c_int,
len: usize,
}

impl Hasher {
/// Creates a new `Hasher` which will use the given cryptographic `algorithm`.
pub fn new(algorithm: CCDigestAlgorithm) -> Hasher {
unsafe {
let mut out = match algorithm {
CCDigestAlgorithm::kCCDigestMD5 => Hasher {
state: State::Reset,
ctx: AlgoData::MD5(CC_MD5_CTX::default()),
init_f: std::mem::transmute(CC_MD5_Init as usize),
update_f: std::mem::transmute(CC_MD5_Update as usize),
final_f: std::mem::transmute(CC_MD5_Final as usize),
len: MD5_DIGEST_LENGTH,
},
CCDigestAlgorithm::kCCDigestSHA1 => Hasher {
state: State::Reset,
ctx: AlgoData::SHA(CC_SHA512_CTX::default()),
init_f: std::mem::transmute(CC_SHA1_Init as usize),
update_f: std::mem::transmute(CC_SHA1_Update as usize),
final_f: std::mem::transmute(CC_SHA1_Final as usize),
len: SHA1_DIGEST_LENGTH,
},
CCDigestAlgorithm::kCCDigestSHA256 => Hasher {
state: State::Reset,
ctx: AlgoData::SHA(CC_SHA512_CTX::default()),
init_f: std::mem::transmute(CC_SHA256_Init as usize),
update_f: std::mem::transmute(CC_SHA256_Update as usize),
final_f: std::mem::transmute(CC_SHA256_Final as usize),
len: SHA256_DIGEST_LENGTH,
},
CCDigestAlgorithm::kCCDigestSHA384 => Hasher {
state: State::Reset,
ctx: AlgoData::SHA(CC_SHA512_CTX::default()),
init_f: std::mem::transmute(CC_SHA384_Init as usize),
update_f: std::mem::transmute(CC_SHA384_Update as usize),
final_f: std::mem::transmute(CC_SHA384_Final as usize),
len: SHA384_DIGEST_LENGTH,
},
CCDigestAlgorithm::kCCDigestSHA512 => Hasher {
state: State::Reset,
ctx: AlgoData::SHA(CC_SHA512_CTX::default()),
init_f: std::mem::transmute(CC_SHA512_Init as usize),
update_f: std::mem::transmute(CC_SHA512_Update as usize),
final_f: std::mem::transmute(CC_SHA512_Final as usize),
len: SHA512_DIGEST_LENGTH,
},
_ => Hasher {
state: State::Reset,
ctx: AlgoData::None,
init_f: std::mem::transmute(CC_SHA512_Init as usize),
update_f: std::mem::transmute(CC_SHA512_Update as usize),
final_f: std::mem::transmute(CC_SHA512_Final as usize),
len: 0,
},
};
(out.init_f)(out.ctx.as_ptr());
out
}
}

fn init(&mut self) {
match self.state {
State::Reset => return,
State::Updated => {
let _ = self.finish();
}
State::Finalized => (),
}
match self.ctx {
AlgoData::MD5(_) => {
self.ctx = AlgoData::MD5(CC_MD5_CTX::default());
}
AlgoData::SHA(_) => {
self.ctx = AlgoData::SHA(CC_SHA512_CTX::default());
}
AlgoData::None => {}
}
self.state = State::Reset;
unsafe { (self.init_f)(self.ctx.as_ptr()) };
}

/// Feeds data into the hasher.
pub fn update(&mut self, data: &[u8]) -> io::Result<usize> {
if self.state == State::Finalized {
self.init();
}
match self.ctx {
AlgoData::None => {
return Ok(0);
}
_ => {}
}
let result =
unsafe { (self.update_f)(self.ctx.as_ptr(), data.as_ptr() as *mut _, data.len()) };
if result == 1 {
self.state = State::Updated;
Ok(data.len())
} else {
err_from_ccdigest_retval!("digest_update", result)
}
}

/// Finalizes digest operations and produces the digest output.
pub fn finish(&mut self) -> io::Result<Vec<u8>> {
if self.state == State::Finalized {
self.init();
}
match self.ctx {
AlgoData::None => {
return Ok(Vec::new());
}
_ => {}
}
let mut md = vec![0; MAX_DIGEST_SIZE];
// let result = unsafe { CCDigestFinal(self.ctx, md.as_mut_ptr()) };
let result = unsafe { (self.final_f)(md.as_mut_ptr(), self.ctx.as_ptr()) };
if result == 1 {
self.state = State::Finalized;
md.truncate(self.len);
Ok(md)
} else {
err_from_ccdigest_retval!("digest_final", result)
}
}
}

impl io::Write for Hasher {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.update(buf)
}

fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

impl Drop for Hasher {
fn drop(&mut self) {
if self.state != State::Finalized {
let _ = self.finish();
}
// unsafe { CCDigestDestroy(self.ctx) }
}
}
8 changes: 8 additions & 0 deletions commoncrypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
extern crate commoncrypto_sys;

#[warn(missing_docs)]
#[cfg(all(target_os = "macos", not(feature = "ios-compat")))]
pub mod hash;
#[warn(missing_docs)]
#[cfg(all(target_os = "macos", not(feature = "ios-compat")))]
pub mod pbkdf2;

#[warn(missing_docs)]
#[cfg(any(target_os = "ios", feature = "ios-compat"))]
pub mod ios;
#[cfg(any(target_os = "ios", feature = "ios-compat"))]
pub use ios as hash;