Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  pkcs11.rs   Sprache: unbekannt

 
/* -*- Mode: rust; rust-indent-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(non_snake_case)]

use pkcs11_bindings::*;
use std::slice;

use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Mutex, MutexGuard};

use crate::internal::{get_attribute, search};
use crate::internal::{ObjectHandle, Query, SearchResult};

use crate::version::*;

const BUILTINS_VERSION: CK_VERSION = CK_VERSION {
    major: NSS_BUILTINS_LIBRARY_VERSION_MAJOR,
    minor: NSS_BUILTINS_LIBRARY_VERSION_MINOR,
};

const FIRMWARE_VERSION: CK_VERSION = CK_VERSION {
    major: NSS_BUILTINS_FIRMWARE_VERSION_MAJOR,
    minor: NSS_BUILTINS_FIRMWARE_VERSION_MINOR,
};

const CRYPTOKI_VERSION: CK_VERSION = CK_VERSION {
    major: NSS_BUILTINS_CRYPTOKI_VERSION_MAJOR,
    minor: NSS_BUILTINS_CRYPTOKI_VERSION_MINOR,
};
const HARDWARE_VERSION: CK_VERSION = CK_VERSION {
    major: NSS_BUILTINS_HARDWARE_VERSION_MAJOR,
    minor: NSS_BUILTINS_HARDWARE_VERSION_MINOR,
};

const MANUFACTURER_ID_BYTES: &[u8; 32] = b"Mozilla Foundation              ";
const LIBRARY_DESCRIPTION_BYTES: &[u8; 32] = b"NSS Builtin Object Cryptoki Modu";

const SLOT_COUNT: CK_ULONG = 1;
const SLOT_ID_ROOTS: CK_SLOT_ID = 1;
const SLOT_DESCRIPTION_ROOTS_BYTES: &[u8; 64] =
    b"NSS Builtin Objects                                             ";

const TOKEN_LABEL_ROOTS_BYTES: &[u8; 32] = b"Builtin Object Token            ";
const TOKEN_MODEL_BYTES: &[u8; 16] = b"1               ";
const TOKEN_SERIAL_NUMBER_BYTES: &[u8; 16] = b"1               ";
const TOKEN_UTC_TIME: &[u8; 16] = b"                ";

#[derive(Debug)]
struct PK11Error(CK_RV);

// The token assigns session handles using a counter. It would make sense to use a 64 bit counter,
// as there would then be no risk of exhausting the session handle space. However,
// CK_SESSION_HANDLE is defined as a C unsigned long, which is a u32 on some platforms.
//
// We start the counter at 1 since PKCS#11 reserves 0 to signal an invalid handle
//
type SessionHandle = u32;
static NEXT_HANDLE: AtomicU32 = AtomicU32::new(1);

// The token needs to keep track of which sessions are open.
//
type SessionSet = BTreeSet<SessionHandle>;
static OPEN_SESSIONS: Mutex<Option<SessionSet>> = Mutex::new(None);

// Helper functions for accessing OPEN_SESSIONS
//
type SessionSetGuard = MutexGuard<'static, Option<SessionSet>>;

fn get_open_sessions_guard() -> Result<SessionSetGuard, PK11Error> {
    OPEN_SESSIONS
        .lock()
        .map_err(|_| PK11Error(CKR_DEVICE_ERROR))
}

fn get_open_sessions(guard: &mut SessionSetGuard) -> Result<&mut SessionSet, PK11Error> {
    let sessions = guard
        .as_mut()
        .ok_or(PK11Error(CKR_CRYPTOKI_NOT_INITIALIZED))?;
    Ok(sessions)
}

// The token needs to cache search results until the client reads them or closes the session.
//
type SearchCache = BTreeMap<SessionHandle, SearchResult>;
static SEARCHES: Mutex<Option<SearchCache>> = Mutex::new(None);

// Helper functions for accessing SEARCHES
//
type SearchCacheGuard = MutexGuard<'static, Option<SearchCache>>;

fn get_search_cache_guard() -> Result<SearchCacheGuard, PK11Error> {
    SEARCHES.lock().map_err(|_| PK11Error(CKR_DEVICE_ERROR))
}

fn get_search_cache(guard: &mut SearchCacheGuard) -> Result<&mut SearchCache, PK11Error> {
    let searches = guard
        .as_mut()
        .ok_or(PK11Error(CKR_CRYPTOKI_NOT_INITIALIZED))?;
    Ok(searches)
}

fn validate_session(handle: SessionHandle) -> Result<(), PK11Error> {
    let mut guard = get_open_sessions_guard()?;
    let sessions = get_open_sessions(&mut guard)?;
    if sessions.contains(&handle) {
        return Ok(());
    }
    if handle < NEXT_HANDLE.load(Ordering::SeqCst) {
        Err(PK11Error(CKR_SESSION_CLOSED))
    } else {
        // Possible that NEXT_HANDLE wrapped and we should return CKR_SESSION_CLOSED.
        // But this is best-effort.
        Err(PK11Error(CKR_SESSION_HANDLE_INVALID))
    }
}

// The internal implementation of C_Initialize
fn initialize() -> Result<(), PK11Error> {
    {
        let mut search_cache_guard = get_search_cache_guard()?;
        if (*search_cache_guard).is_some() {
            return Err(PK11Error(CKR_CRYPTOKI_ALREADY_INITIALIZED));
        }
        *search_cache_guard = Some(SearchCache::default());
    }

    {
        let mut session_guard = get_open_sessions_guard()?;
        if (*session_guard).is_some() {
            return Err(PK11Error(CKR_CRYPTOKI_ALREADY_INITIALIZED));
        }
        *session_guard = Some(SessionSet::default());
    }

    Ok(())
}

// The internal implementation of C_Finalize
fn finalize() -> Result<(), PK11Error> {
    {
        let mut guard = get_search_cache_guard()?;
        // Try to access the search cache to ensure we're initialized.
        // Returns CKR_CRYPTOKI_NOT_INITIALIZED if we're not.
        let _ = get_search_cache(&mut guard)?;
        *guard = None;
    }

    let mut guard = get_open_sessions_guard()?;
    let _ = get_open_sessions(&mut guard)?;
    *guard = None;

    Ok(())
}

// Internal implementation of C_OpenSession
fn open_session() -> Result<SessionHandle, PK11Error> {
    let mut handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
    if handle == 0 {
        // skip handle 0 if the addition wraps
        handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
    }

    let mut guard = get_open_sessions_guard()?;
    let sessions = get_open_sessions(&mut guard)?;
    while !sessions.insert(handle) {
        // this only executes if NEXT_HANDLE wraps while sessions with
        // small handles are still open.
        handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
    }

    Ok(handle)
}

// Internal implementation of C_CloseSession
fn close_session(session: SessionHandle) -> Result<(), PK11Error> {
    {
        let mut guard = get_search_cache_guard()?;
        let searches = get_search_cache(&mut guard)?;
        searches.remove(&session);
    }

    {
        let mut guard = get_open_sessions_guard()?;
        let sessions = get_open_sessions(&mut guard)?;
        if sessions.remove(&session) {
            Ok(())
        } else if session < NEXT_HANDLE.load(Ordering::SeqCst) {
            Err(PK11Error(CKR_SESSION_CLOSED))
        } else {
            Err(PK11Error(CKR_SESSION_HANDLE_INVALID))
        }
    }
}

// Internal implementation of C_CloseAllSessions
fn close_all_sessions() -> Result<(), PK11Error> {
    {
        let mut guard = get_search_cache_guard()?;
        let searches = get_search_cache(&mut guard)?;
        searches.clear();
    }

    {
        let mut guard = get_open_sessions_guard()?;
        let sessions = get_open_sessions(&mut guard)?;
        sessions.clear();
    }

    Ok(())
}

// Internal implementation of C_FindObjectsInit
fn find_objects_init(session: SessionHandle, query: &Query) -> Result<usize, PK11Error> {
    validate_session(session)?;

    let results = search(query);
    let count = results.len();

    let mut guard = get_search_cache_guard()?;
    let searches = get_search_cache(&mut guard)?;
    match searches.entry(session) {
        Entry::Occupied(_) => Err(PK11Error(CKR_OPERATION_ACTIVE)),
        Entry::Vacant(v) => {
            v.insert(results);
            Ok(count)
        }
    }
}

// Internal implementation of C_FindObjects
fn find_objects(session: SessionHandle, out: &mut [CK_OBJECT_HANDLE]) -> Result<usize, PK11Error> {
    validate_session(session)?;

    let mut guard = get_search_cache_guard()?;
    let searches = get_search_cache(&mut guard)?;
    if let Some(objects) = searches.get_mut(&session) {
        for (i, out_i) in out.iter_mut().enumerate() {
            match objects.pop() {
                Some(object) => *out_i = object.into(),
                None => return Ok(i),
            }
        }
        Ok(out.len())
    } else {
        Ok(0)
    }
}

// Internal implementation of C_FindObjectsFinal
fn find_objects_final(session: SessionHandle) -> Result<(), PK11Error> {
    validate_session(session)?;

    let mut guard = get_search_cache_guard()?;
    let searches = get_search_cache(&mut guard)?;
    searches.remove(&session);
    Ok(())
}

extern "C" fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV {
    match initialize() {
        Ok(_) => CKR_OK,
        Err(PK11Error(e)) => e,
    }
}

extern "C" fn C_Finalize(pReserved: CK_VOID_PTR) -> CK_RV {
    if !pReserved.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    match finalize() {
        Ok(_) => CKR_OK,
        Err(PK11Error(e)) => e,
    }
}

extern "C" fn C_GetInfo(pInfo: CK_INFO_PTR) -> CK_RV {
    if pInfo.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    unsafe {
        *pInfo = CK_INFO {
            cryptokiVersion: CRYPTOKI_VERSION,
            manufacturerID: *MANUFACTURER_ID_BYTES,
            flags: 0,
            libraryDescription: *LIBRARY_DESCRIPTION_BYTES,
            libraryVersion: BUILTINS_VERSION,
        };
    }
    CKR_OK
}

extern "C" fn C_GetSlotList(
    _tokenPresent: CK_BBOOL,
    pSlotList: CK_SLOT_ID_PTR,
    pulCount: CK_ULONG_PTR,
) -> CK_RV {
    if pulCount.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    if !pSlotList.is_null() {
        if unsafe { *pulCount } < SLOT_COUNT {
            return CKR_BUFFER_TOO_SMALL;
        }
        unsafe {
            *pSlotList = SLOT_ID_ROOTS;
        }
    }
    unsafe {
        *pulCount = SLOT_COUNT;
    }
    CKR_OK
}

extern "C" fn C_GetSlotInfo(slotID: CK_SLOT_ID, pInfo: CK_SLOT_INFO_PTR) -> CK_RV {
    if (slotID != SLOT_ID_ROOTS) || pInfo.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    unsafe {
        *pInfo = CK_SLOT_INFO {
            slotDescription: *SLOT_DESCRIPTION_ROOTS_BYTES,
            manufacturerID: *MANUFACTURER_ID_BYTES,
            flags: CKF_TOKEN_PRESENT,
            hardwareVersion: HARDWARE_VERSION,
            firmwareVersion: FIRMWARE_VERSION,
        };
    }
    CKR_OK
}

extern "C" fn C_GetTokenInfo(slotID: CK_SLOT_ID, pInfo: CK_TOKEN_INFO_PTR) -> CK_RV {
    if (slotID != SLOT_ID_ROOTS) || pInfo.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    unsafe {
        *pInfo = CK_TOKEN_INFO {
            label: *TOKEN_LABEL_ROOTS_BYTES,
            manufacturerID: *MANUFACTURER_ID_BYTES,
            model: *TOKEN_MODEL_BYTES,
            serialNumber: *TOKEN_SERIAL_NUMBER_BYTES,
            flags: CKF_WRITE_PROTECTED,
            ulMaxSessionCount: CK_UNAVAILABLE_INFORMATION,
            ulSessionCount: 0,
            ulMaxRwSessionCount: CK_UNAVAILABLE_INFORMATION,
            ulRwSessionCount: 0,
            ulMaxPinLen: CK_UNAVAILABLE_INFORMATION,
            ulMinPinLen: CK_UNAVAILABLE_INFORMATION,
            ulTotalPublicMemory: CK_UNAVAILABLE_INFORMATION,
            ulFreePublicMemory: CK_UNAVAILABLE_INFORMATION,
            ulTotalPrivateMemory: CK_UNAVAILABLE_INFORMATION,
            ulFreePrivateMemory: CK_UNAVAILABLE_INFORMATION,
            hardwareVersion: HARDWARE_VERSION,
            firmwareVersion: FIRMWARE_VERSION,
            utcTime: *TOKEN_UTC_TIME,
        };
    }
    CKR_OK
}

extern "C" fn C_GetMechanismList(
    slotID: CK_SLOT_ID,
    _pMechanismList: CK_MECHANISM_TYPE_PTR,
    pulCount: CK_ULONG_PTR,
) -> CK_RV {
    if slotID != SLOT_ID_ROOTS || pulCount.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    unsafe {
        *pulCount = 0;
    }
    CKR_OK
}

extern "C" fn C_GetMechanismInfo(
    _slotID: CK_SLOT_ID,
    _type: CK_MECHANISM_TYPE,
    _pInfo: CK_MECHANISM_INFO_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_InitToken(
    _slotID: CK_SLOT_ID,
    _pPin: CK_UTF8CHAR_PTR,
    _ulPinLen: CK_ULONG,
    _pLabel: CK_UTF8CHAR_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_InitPIN(
    _hSession: CK_SESSION_HANDLE,
    _pPin: CK_UTF8CHAR_PTR,
    _ulPinLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SetPIN(
    _hSession: CK_SESSION_HANDLE,
    _pOldPin: CK_UTF8CHAR_PTR,
    _ulOldLen: CK_ULONG,
    _pNewPin: CK_UTF8CHAR_PTR,
    _ulNewLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_OpenSession(
    slotID: CK_SLOT_ID,
    flags: CK_FLAGS,
    _pApplication: CK_VOID_PTR,
    _Notify: CK_NOTIFY,
    phSession: CK_SESSION_HANDLE_PTR,
) -> CK_RV {
    if slotID != SLOT_ID_ROOTS || phSession.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    // [pkcs11-base-v3.0, Section 5.6.1]
    // For legacy reasons, the CKF_SERIAL_SESSION bit MUST always be set; if a call to
    // C_OpenSession does not have this bit set, the call should return unsuccessfully with the
    // error code CKR_SESSION_PARALLEL_NOT_SUPPORTED.
    if flags & CKF_SERIAL_SESSION == 0 {
        return CKR_SESSION_PARALLEL_NOT_SUPPORTED;
    }
    let session_id = match open_session() {
        Ok(session_id) => session_id as CK_SESSION_HANDLE,
        Err(PK11Error(e)) => return e,
    };
    unsafe { *phSession = session_id };
    CKR_OK
}

extern "C" fn C_CloseSession(hSession: CK_SESSION_HANDLE) -> CK_RV {
    let session: SessionHandle = match hSession.try_into() {
        Ok(session) => session,
        Err(_) => return CKR_SESSION_HANDLE_INVALID,
    };
    match close_session(session) {
        Ok(_) => CKR_OK,
        Err(PK11Error(e)) => e,
    }
}

extern "C" fn C_CloseAllSessions(slotID: CK_SLOT_ID) -> CK_RV {
    if slotID != SLOT_ID_ROOTS {
        return CKR_ARGUMENTS_BAD;
    }
    match close_all_sessions() {
        Ok(_) => CKR_OK,
        Err(PK11Error(e)) => e,
    }
}

extern "C" fn C_GetSessionInfo(_hSession: CK_SESSION_HANDLE, _pInfo: CK_SESSION_INFO_PTR) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_GetOperationState(
    _hSession: CK_SESSION_HANDLE,
    _pOperationState: CK_BYTE_PTR,
    _pulOperationStateLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SetOperationState(
    _hSession: CK_SESSION_HANDLE,
    _pOperationState: CK_BYTE_PTR,
    _ulOperationStateLen: CK_ULONG,
    _hEncryptionKey: CK_OBJECT_HANDLE,
    _hAuthenticationKey: CK_OBJECT_HANDLE,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_Login(
    _hSession: CK_SESSION_HANDLE,
    _userType: CK_USER_TYPE,
    _pPin: CK_UTF8CHAR_PTR,
    _ulPinLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_Logout(_hSession: CK_SESSION_HANDLE) -> CK_RV {
    CKR_OK
}

extern "C" fn C_CreateObject(
    _hSession: CK_SESSION_HANDLE,
    _pTemplate: CK_ATTRIBUTE_PTR,
    _ulCount: CK_ULONG,
    _phObject: CK_OBJECT_HANDLE_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_CopyObject(
    _hSession: CK_SESSION_HANDLE,
    _hObject: CK_OBJECT_HANDLE,
    _pTemplate: CK_ATTRIBUTE_PTR,
    _ulCount: CK_ULONG,
    _phNewObject: CK_OBJECT_HANDLE_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DestroyObject(_hSession: CK_SESSION_HANDLE, _hObject: CK_OBJECT_HANDLE) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_GetObjectSize(
    _hSession: CK_SESSION_HANDLE,
    _hObject: CK_OBJECT_HANDLE,
    _pulSize: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_GetAttributeValue(
    _hSession: CK_SESSION_HANDLE,
    hObject: CK_OBJECT_HANDLE,
    pTemplate: CK_ATTRIBUTE_PTR,
    ulCount: CK_ULONG,
) -> CK_RV {
    if pTemplate.is_null() {
        return CKR_ARGUMENTS_BAD;
    }

    let count: usize = match ulCount.try_into() {
        Ok(count) => count,
        Err(_) => return CKR_ARGUMENTS_BAD,
    };

    // C_GetAttributeValue has a session handle parameter because PKCS#11 objects can have
    // session-bound lifetimes and access controls. We don't have any session objects, and all of
    // our token objects are public. So there's no good reason to validate the session handle.
    //
    //let session: SessionHandle = match hSession.try_into() {
    //    Ok(session) => session,
    //    Err(_) => return CKR_SESSION_HANDLE_INVALID,
    //};
    //
    //if let Err(PK11Error(e)) = validate_session(session) {
    //    return e;
    //}

    let handle: ObjectHandle = match hObject.try_into() {
        Ok(handle) => handle,
        Err(_) => return CKR_OBJECT_HANDLE_INVALID,
    };

    let attrs: &mut [CK_ATTRIBUTE] = unsafe { slice::from_raw_parts_mut(pTemplate, count) };

    let mut rv = CKR_OK;

    // Handle requests with null pValue fields
    for attr in attrs.iter_mut().filter(|x| x.pValue.is_null()) {
        attr.ulValueLen = match get_attribute(attr.type_, &handle) {
            None => {
                // [pkcs11-base-v3.0, Section 5.7.5]
                // 2. [...] if the specified value for the object is invalid (the object does not possess
                //    such an attribute), then the ulValueLen field in that triple is modified to hold the
                //    value CK_UNAVAILABLE_INFORMATION.
                rv = CKR_ATTRIBUTE_TYPE_INVALID;
                CK_UNAVAILABLE_INFORMATION
            }
            Some(attr) => {
                // [pkcs11-base-v3.0, Section 5.7.5]
                // 3. [...] if the pValue field has the value NULL_PTR, then the ulValueLen field is modified
                //    to hold the exact length of the specified attribute for the object.
                attr.len() as CK_ULONG
            }
        }
    }

    // Handle requests with non-null pValue fields
    for attr in attrs.iter_mut().filter(|x| !x.pValue.is_null()) {
        let dst_len: usize = match attr.ulValueLen.try_into() {
            Ok(dst_len) => dst_len,
            Err(_) => return CKR_ARGUMENTS_BAD,
        };
        attr.ulValueLen = match get_attribute(attr.type_, &handle) {
            None => {
                // [pkcs11-base-v3.0, Section 5.7.5]
                // 2. [...] if the specified value for the object is invalid (the object does not possess
                //    such an attribute), then the ulValueLen field in that triple is modified to hold the
                //    value CK_UNAVAILABLE_INFORMATION.
                rv = CKR_ATTRIBUTE_TYPE_INVALID;
                CK_UNAVAILABLE_INFORMATION
            }
            Some(src) if dst_len >= src.len() => {
                // [pkcs11-base-v3.0, Section 5.7.5]
                // 4. [...] if the length specified in ulValueLen is large enough to hold the value
                //    of the specified attribute for the object, then that attribute is copied into
                //    the buffer located at pValue, and the ulValueLen field is modified to hold
                //    the exact length of the attribute.
                let dst: &mut [u8] =
                    unsafe { slice::from_raw_parts_mut(attr.pValue as *mut u8, dst_len) };
                dst[..src.len()].copy_from_slice(src);
                src.len() as CK_ULONG
            }
            _ => {
                // [pkcs11-base-v3.0, Section 5.7.5]
                // 5. Otherwise, the ulValueLen field is modified to hold the value
                //    CK_UNAVAILABLE_INFORMATION.
                rv = CKR_BUFFER_TOO_SMALL;
                CK_UNAVAILABLE_INFORMATION
            }
        };
    }

    // [pkcs11-base-v3.0, Section 5.7.5]
    // If case 2 applies to any of the requested attributes, then the call should return the value
    // CKR_ATTRIBUTE_TYPE_INVALID.  If case 5 applies to any of the requested attributes, then the
    // call should return the value CKR_BUFFER_TOO_SMALL.  As usual, if more than one of these
    // error codes is applicable, Cryptoki may return any of them.  Only if none of them applies to
    // any of the requested attributes will CKR_OK be returned.
    rv
}

extern "C" fn C_SetAttributeValue(
    _hSession: CK_SESSION_HANDLE,
    _hObject: CK_OBJECT_HANDLE,
    _pTemplate: CK_ATTRIBUTE_PTR,
    _ulCount: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_FindObjectsInit(
    hSession: CK_SESSION_HANDLE,
    pTemplate: CK_ATTRIBUTE_PTR,
    ulCount: CK_ULONG,
) -> CK_RV {
    if pTemplate.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    let count: usize = match ulCount.try_into() {
        Ok(count) => count,
        Err(_) => return CKR_ARGUMENTS_BAD,
    };
    let session: SessionHandle = match hSession.try_into() {
        Ok(session) => session,
        Err(_) => return CKR_SESSION_HANDLE_INVALID,
    };

    let raw_attrs: &[CK_ATTRIBUTE] = unsafe { slice::from_raw_parts_mut(pTemplate, count) };

    let mut query: Vec<(CK_ATTRIBUTE_TYPE, &[u8])> = Vec::with_capacity(raw_attrs.len());
    for attr in raw_attrs {
        match usize::try_from(attr.ulValueLen) {
            Ok(len) => query.push((attr.type_, unsafe {
                slice::from_raw_parts_mut(attr.pValue as *mut u8, len)
            })),
            Err(_) => return CKR_ARGUMENTS_BAD,
        }
    }

    match find_objects_init(session, &query) {
        Ok(_) => CKR_OK,
        Err(PK11Error(e)) => e,
    }
}

extern "C" fn C_FindObjects(
    hSession: CK_SESSION_HANDLE,
    phObject: CK_OBJECT_HANDLE_PTR,
    ulMaxObjectCount: CK_ULONG,
    pulObjectCount: CK_ULONG_PTR,
) -> CK_RV {
    if phObject.is_null() || pulObjectCount.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    let max_object_count: usize = match ulMaxObjectCount.try_into() {
        Ok(max_object_count) => max_object_count,
        Err(_) => return CKR_ARGUMENTS_BAD,
    };
    let session: SessionHandle = match hSession.try_into() {
        Ok(session) => session,
        Err(_) => return CKR_SESSION_HANDLE_INVALID,
    };
    let out: &mut [CK_OBJECT_HANDLE] =
        unsafe { slice::from_raw_parts_mut(phObject, max_object_count) };
    match find_objects(session, out) {
        Ok(num_found) => {
            unsafe { *pulObjectCount = num_found as CK_ULONG };
            CKR_OK
        }
        Err(PK11Error(e)) => e,
    }
}

extern "C" fn C_FindObjectsFinal(hSession: CK_SESSION_HANDLE) -> CK_RV {
    let session: SessionHandle = match hSession.try_into() {
        Ok(session) => session,
        Err(_) => return CKR_SESSION_HANDLE_INVALID,
    };
    match find_objects_final(session) {
        Ok(()) => CKR_OK,
        Err(PK11Error(e)) => e,
    }
}

extern "C" fn C_EncryptInit(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hKey: CK_OBJECT_HANDLE,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_Encrypt(
    _hSession: CK_SESSION_HANDLE,
    _pData: CK_BYTE_PTR,
    _ulDataLen: CK_ULONG,
    _pEncryptedData: CK_BYTE_PTR,
    _pulEncryptedDataLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_EncryptUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pPart: CK_BYTE_PTR,
    _ulPartLen: CK_ULONG,
    _pEncryptedPart: CK_BYTE_PTR,
    _pulEncryptedPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_EncryptFinal(
    _hSession: CK_SESSION_HANDLE,
    _pLastEncryptedPart: CK_BYTE_PTR,
    _pulLastEncryptedPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DecryptInit(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hKey: CK_OBJECT_HANDLE,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_Decrypt(
    _hSession: CK_SESSION_HANDLE,
    _pEncryptedData: CK_BYTE_PTR,
    _ulEncryptedDataLen: CK_ULONG,
    _pData: CK_BYTE_PTR,
    _pulDataLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DecryptUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pEncryptedPart: CK_BYTE_PTR,
    _ulEncryptedPartLen: CK_ULONG,
    _pPart: CK_BYTE_PTR,
    _pulPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DecryptFinal(
    _hSession: CK_SESSION_HANDLE,
    _pLastPart: CK_BYTE_PTR,
    _pulLastPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DigestInit(_hSession: CK_SESSION_HANDLE, _pMechanism: CK_MECHANISM_PTR) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_Digest(
    _hSession: CK_SESSION_HANDLE,
    _pData: CK_BYTE_PTR,
    _ulDataLen: CK_ULONG,
    _pDigest: CK_BYTE_PTR,
    _pulDigestLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DigestUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pPart: CK_BYTE_PTR,
    _ulPartLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DigestKey(_hSession: CK_SESSION_HANDLE, _hKey: CK_OBJECT_HANDLE) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DigestFinal(
    _hSession: CK_SESSION_HANDLE,
    _pDigest: CK_BYTE_PTR,
    _pulDigestLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SignInit(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hKey: CK_OBJECT_HANDLE,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_Sign(
    _hSession: CK_SESSION_HANDLE,
    _pData: CK_BYTE_PTR,
    _ulDataLen: CK_ULONG,
    _pSignature: CK_BYTE_PTR,
    _pulSignatureLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SignUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pPart: CK_BYTE_PTR,
    _ulPartLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SignFinal(
    _hSession: CK_SESSION_HANDLE,
    _pSignature: CK_BYTE_PTR,
    _pulSignatureLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SignRecoverInit(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hKey: CK_OBJECT_HANDLE,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SignRecover(
    _hSession: CK_SESSION_HANDLE,
    _pData: CK_BYTE_PTR,
    _ulDataLen: CK_ULONG,
    _pSignature: CK_BYTE_PTR,
    _pulSignatureLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_VerifyInit(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hKey: CK_OBJECT_HANDLE,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_Verify(
    _hSession: CK_SESSION_HANDLE,
    _pData: CK_BYTE_PTR,
    _ulDataLen: CK_ULONG,
    _pSignature: CK_BYTE_PTR,
    _ulSignatureLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_VerifyUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pPart: CK_BYTE_PTR,
    _ulPartLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_VerifyFinal(
    _hSession: CK_SESSION_HANDLE,
    _pSignature: CK_BYTE_PTR,
    _ulSignatureLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_VerifyRecoverInit(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hKey: CK_OBJECT_HANDLE,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_VerifyRecover(
    _hSession: CK_SESSION_HANDLE,
    _pSignature: CK_BYTE_PTR,
    _ulSignatureLen: CK_ULONG,
    _pData: CK_BYTE_PTR,
    _pulDataLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DigestEncryptUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pPart: CK_BYTE_PTR,
    _ulPartLen: CK_ULONG,
    _pEncryptedPart: CK_BYTE_PTR,
    _pulEncryptedPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DecryptDigestUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pEncryptedPart: CK_BYTE_PTR,
    _ulEncryptedPartLen: CK_ULONG,
    _pPart: CK_BYTE_PTR,
    _pulPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SignEncryptUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pPart: CK_BYTE_PTR,
    _ulPartLen: CK_ULONG,
    _pEncryptedPart: CK_BYTE_PTR,
    _pulEncryptedPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DecryptVerifyUpdate(
    _hSession: CK_SESSION_HANDLE,
    _pEncryptedPart: CK_BYTE_PTR,
    _ulEncryptedPartLen: CK_ULONG,
    _pPart: CK_BYTE_PTR,
    _pulPartLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_GenerateKey(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _pTemplate: CK_ATTRIBUTE_PTR,
    _ulCount: CK_ULONG,
    _phKey: CK_OBJECT_HANDLE_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_GenerateKeyPair(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _pPublicKeyTemplate: CK_ATTRIBUTE_PTR,
    _ulPublicKeyAttributeCount: CK_ULONG,
    _pPrivateKeyTemplate: CK_ATTRIBUTE_PTR,
    _ulPrivateKeyAttributeCount: CK_ULONG,
    _phPublicKey: CK_OBJECT_HANDLE_PTR,
    _phPrivateKey: CK_OBJECT_HANDLE_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_WrapKey(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hWrappingKey: CK_OBJECT_HANDLE,
    _hKey: CK_OBJECT_HANDLE,
    _pWrappedKey: CK_BYTE_PTR,
    _pulWrappedKeyLen: CK_ULONG_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_UnwrapKey(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hUnwrappingKey: CK_OBJECT_HANDLE,
    _pWrappedKey: CK_BYTE_PTR,
    _ulWrappedKeyLen: CK_ULONG,
    _pTemplate: CK_ATTRIBUTE_PTR,
    _ulAttributeCount: CK_ULONG,
    _phKey: CK_OBJECT_HANDLE_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_DeriveKey(
    _hSession: CK_SESSION_HANDLE,
    _pMechanism: CK_MECHANISM_PTR,
    _hBaseKey: CK_OBJECT_HANDLE,
    _pTemplate: CK_ATTRIBUTE_PTR,
    _ulAttributeCount: CK_ULONG,
    _phKey: CK_OBJECT_HANDLE_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_SeedRandom(
    _hSession: CK_SESSION_HANDLE,
    _pSeed: CK_BYTE_PTR,
    _ulSeedLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_GenerateRandom(
    _hSession: CK_SESSION_HANDLE,
    _RandomData: CK_BYTE_PTR,
    _ulRandomLen: CK_ULONG,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_GetFunctionStatus(_hSession: CK_SESSION_HANDLE) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_CancelFunction(_hSession: CK_SESSION_HANDLE) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

extern "C" fn C_WaitForSlotEvent(
    _flags: CK_FLAGS,
    _pSlot: CK_SLOT_ID_PTR,
    _pRserved: CK_VOID_PTR,
) -> CK_RV {
    CKR_FUNCTION_NOT_SUPPORTED
}

pub static FUNCTION_LIST: CK_FUNCTION_LIST = CK_FUNCTION_LIST {
    version: CRYPTOKI_VERSION,
    C_Initialize: Some(C_Initialize),
    C_Finalize: Some(C_Finalize),
    C_GetInfo: Some(C_GetInfo),
    C_GetFunctionList: None,
    C_GetSlotList: Some(C_GetSlotList),
    C_GetSlotInfo: Some(C_GetSlotInfo),
    C_GetTokenInfo: Some(C_GetTokenInfo),
    C_GetMechanismList: Some(C_GetMechanismList),
    C_GetMechanismInfo: Some(C_GetMechanismInfo),
    C_InitToken: Some(C_InitToken),
    C_InitPIN: Some(C_InitPIN),
    C_SetPIN: Some(C_SetPIN),
    C_OpenSession: Some(C_OpenSession),
    C_CloseSession: Some(C_CloseSession),
    C_CloseAllSessions: Some(C_CloseAllSessions),
    C_GetSessionInfo: Some(C_GetSessionInfo),
    C_GetOperationState: Some(C_GetOperationState),
    C_SetOperationState: Some(C_SetOperationState),
    C_Login: Some(C_Login),
    C_Logout: Some(C_Logout),
    C_CreateObject: Some(C_CreateObject),
    C_CopyObject: Some(C_CopyObject),
    C_DestroyObject: Some(C_DestroyObject),
    C_GetObjectSize: Some(C_GetObjectSize),
    C_GetAttributeValue: Some(C_GetAttributeValue),
    C_SetAttributeValue: Some(C_SetAttributeValue),
    C_FindObjectsInit: Some(C_FindObjectsInit),
    C_FindObjects: Some(C_FindObjects),
    C_FindObjectsFinal: Some(C_FindObjectsFinal),
    C_EncryptInit: Some(C_EncryptInit),
    C_Encrypt: Some(C_Encrypt),
    C_EncryptUpdate: Some(C_EncryptUpdate),
    C_EncryptFinal: Some(C_EncryptFinal),
    C_DecryptInit: Some(C_DecryptInit),
    C_Decrypt: Some(C_Decrypt),
    C_DecryptUpdate: Some(C_DecryptUpdate),
    C_DecryptFinal: Some(C_DecryptFinal),
    C_DigestInit: Some(C_DigestInit),
    C_Digest: Some(C_Digest),
    C_DigestUpdate: Some(C_DigestUpdate),
    C_DigestKey: Some(C_DigestKey),
    C_DigestFinal: Some(C_DigestFinal),
    C_SignInit: Some(C_SignInit),
    C_Sign: Some(C_Sign),
    C_SignUpdate: Some(C_SignUpdate),
    C_SignFinal: Some(C_SignFinal),
    C_SignRecoverInit: Some(C_SignRecoverInit),
    C_SignRecover: Some(C_SignRecover),
    C_VerifyInit: Some(C_VerifyInit),
    C_Verify: Some(C_Verify),
    C_VerifyUpdate: Some(C_VerifyUpdate),
    C_VerifyFinal: Some(C_VerifyFinal),
    C_VerifyRecoverInit: Some(C_VerifyRecoverInit),
    C_VerifyRecover: Some(C_VerifyRecover),
    C_DigestEncryptUpdate: Some(C_DigestEncryptUpdate),
    C_DecryptDigestUpdate: Some(C_DecryptDigestUpdate),
    C_SignEncryptUpdate: Some(C_SignEncryptUpdate),
    C_DecryptVerifyUpdate: Some(C_DecryptVerifyUpdate),
    C_GenerateKey: Some(C_GenerateKey),
    C_GenerateKeyPair: Some(C_GenerateKeyPair),
    C_WrapKey: Some(C_WrapKey),
    C_UnwrapKey: Some(C_UnwrapKey),
    C_DeriveKey: Some(C_DeriveKey),
    C_SeedRandom: Some(C_SeedRandom),
    C_GenerateRandom: Some(C_GenerateRandom),
    C_GetFunctionStatus: Some(C_GetFunctionStatus),
    C_CancelFunction: Some(C_CancelFunction),
    C_WaitForSlotEvent: Some(C_WaitForSlotEvent),
};

#[no_mangle]
pub unsafe fn BUILTINSC_GetFunctionList(ppFunctionList: CK_FUNCTION_LIST_PTR_PTR) -> CK_RV {
    if ppFunctionList.is_null() {
        return CKR_ARGUMENTS_BAD;
    }
    // CK_FUNCTION_LIST_PTR is a *mut CK_FUNCTION_LIST, but as per the
    // specification, the caller must treat it as *const CK_FUNCTION_LIST.
    *ppFunctionList = std::ptr::addr_of!(FUNCTION_LIST) as CK_FUNCTION_LIST_PTR;
    CKR_OK
}

#[cfg(test)]
mod pkcs11_tests {
    use crate::certdata::*;
    use crate::internal::*;
    use crate::pkcs11::*;

    #[test]
    fn test_main() {
        // We need to run tests serially because of C_Initialize / C_Finalize calls.
        test_simple();
        test_c_get_function_list();
        test_c_get_attribute();
    }

    fn test_simple() {
        let query = &[(CKA_CLASS, CKO_CERTIFICATE_BYTES)];
        initialize().expect("initialize should not fail.");
        let hSession = open_session().expect("open_session should not fail.");
        let count = find_objects_init(hSession, query).expect("find_objects_init should not fail.");
        assert_eq!(count, BUILTINS.len());
        let mut results: [CK_OBJECT_HANDLE; 10] = [0; 10];
        let n_read =
            find_objects(hSession, &mut results).expect("find_objects_init should not fail.");
        assert_eq!(n_read, 10);
        finalize().expect("finalize should not fail.");
    }

    fn test_c_get_function_list() {
        let c_null = 0 as *mut std::ffi::c_void;
        let mut pFunctionList: CK_FUNCTION_LIST_PTR = c_null as CK_FUNCTION_LIST_PTR;
        let rv = unsafe { crate::pkcs11::BUILTINSC_GetFunctionList(&mut pFunctionList) };
        assert_eq!(CKR_OK, rv);
        if let Some(pC_Initialize) = unsafe { (*pFunctionList).C_Initialize } {
            let rv = unsafe { pC_Initialize(c_null) };
            assert_eq!(CKR_OK, rv);
        } else {
            assert!(false);
        }

        if let Some(pC_Finalize) = unsafe { (*pFunctionList).C_Finalize } {
            let rv = unsafe { pC_Finalize(c_null) };
            assert_eq!(CKR_OK, rv);
        } else {
            assert!(false);
        }
    }

    fn test_c_get_attribute() {
        let c_null = 0 as *mut std::ffi::c_void;
        let template: &mut [CK_ATTRIBUTE] = &mut [CK_ATTRIBUTE {
            type_: CKA_SUBJECT,
            pValue: c_null,
            ulValueLen: 0,
        }];
        let template_ptr = &mut template[0] as CK_ATTRIBUTE_PTR;
        let object: CK_OBJECT_HANDLE = 2;
        let mut session: CK_SESSION_HANDLE = 0;
        assert_eq!(CKR_OK, C_Initialize(c_null));
        assert_eq!(
            CKR_OK,
            C_OpenSession(
                SLOT_ID_ROOTS,
                CKF_SERIAL_SESSION,
                c_null,
                None,
                &mut session as *mut CK_SESSION_HANDLE
            )
        );
        assert_eq!(
            CKR_OK,
            C_GetAttributeValue(session, object, template_ptr, 1)
        );
        let len = template[0].ulValueLen as usize;
        assert_eq!(len, BUILTINS[0].der_name.len());

        let value: &mut [u8] = &mut vec![0; 1];
        let value_ptr: *mut u8 = &mut value[0] as *mut u8;
        template[0].pValue = value_ptr as *mut std::ffi::c_void;
        template[0].ulValueLen = 1;
        assert_eq!(
            CKR_BUFFER_TOO_SMALL,
            C_GetAttributeValue(session, object, template_ptr, 1)
        );
        assert_eq!(template[0].ulValueLen, CK_UNAVAILABLE_INFORMATION);

        let value: &mut [u8] = &mut vec![0; len];
        let value_ptr: *mut u8 = &mut value[0] as *mut u8;
        template[0].pValue = value_ptr as *mut std::ffi::c_void;
        template[0].ulValueLen = len as CK_ULONG;
        assert_eq!(
            CKR_OK,
            C_GetAttributeValue(session, object, template_ptr, 1)
        );
        assert_eq!(value, BUILTINS[0].der_name);
        assert_eq!(CKR_OK, C_Finalize(c_null));
    }
}

[ Dauer der Verarbeitung: 0.34 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge