nss/pk11/
types.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::{
6    error::*,
7    pk11::slot::{generate_random, get_internal_slot},
8    util::{map_nss_secstatus, ScopedPtr},
9};
10use std::{
11    ops::Deref,
12    os::raw::{c_int, c_uchar, c_uint, c_void},
13    ptr,
14};
15
16scoped_ptr!(SymKey, nss_sys::PK11SymKey, nss_sys::PK11_FreeSymKey);
17scoped_ptr!(
18    PrivateKey,
19    nss_sys::SECKEYPrivateKey,
20    nss_sys::SECKEY_DestroyPrivateKey
21);
22scoped_ptr!(
23    PublicKey,
24    nss_sys::SECKEYPublicKey,
25    nss_sys::SECKEY_DestroyPublicKey
26);
27scoped_ptr!(
28    GenericObject,
29    nss_sys::PK11GenericObject,
30    nss_sys::PK11_DestroyGenericObject
31);
32
33scoped_ptr!(
34    Certificate,
35    nss_sys::CERTCertificate,
36    nss_sys::CERT_DestroyCertificate
37);
38
39scoped_ptr!(Context, nss_sys::PK11Context, pk11_destroy_context_true);
40scoped_ptr!(Slot, nss_sys::PK11SlotInfo, nss_sys::PK11_FreeSlot);
41
42scoped_ptr!(
43    AlgorithmID,
44    nss_sys::SECAlgorithmID,
45    secoid_destroy_algorithm_id_true
46);
47
48#[inline]
49unsafe fn secoid_destroy_algorithm_id_true(alg_id: *mut nss_sys::SECAlgorithmID) {
50    nss_sys::SECOID_DestroyAlgorithmID(alg_id, nss_sys::PR_TRUE);
51}
52
53#[inline]
54unsafe fn pk11_destroy_context_true(context: *mut nss_sys::PK11Context) {
55    nss_sys::PK11_DestroyContext(context, nss_sys::PR_TRUE);
56}
57
58// Trait for types that have PCKS#11 attributes that are readable. See
59// https://searchfox.org/mozilla-central/rev/8ed8474757695cdae047150a0eaf94a5f1c96dbe/security/nss/lib/pk11wrap/pk11pub.h#842-864
60/// # Safety
61/// Unsafe since it needs to call [`nss_sys::PK11_ReadRawAttribute`] which is
62/// a C NSS function, and thus inherently unsafe to call
63pub(crate) unsafe trait Pkcs11Object: ScopedPtr {
64    const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType;
65    fn read_raw_attribute(
66        &self,
67        attribute_type: nss_sys::CK_ATTRIBUTE_TYPE,
68    ) -> Result<ScopedSECItem> {
69        let mut out_sec = ScopedSECItem::empty(nss_sys::SECItemType::siBuffer);
70        map_nss_secstatus(|| unsafe {
71            nss_sys::PK11_ReadRawAttribute(
72                Self::PK11_OBJECT_TYPE as u32,
73                self.as_mut_ptr() as *mut c_void,
74                attribute_type,
75                out_sec.as_mut_ref(),
76            )
77        })?;
78        Ok(out_sec)
79    }
80}
81
82unsafe impl Pkcs11Object for GenericObject {
83    const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType = nss_sys::PK11ObjectType::PK11_TypeGeneric;
84}
85unsafe impl Pkcs11Object for PrivateKey {
86    const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType = nss_sys::PK11ObjectType::PK11_TypePrivKey;
87}
88unsafe impl Pkcs11Object for PublicKey {
89    const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType = nss_sys::PK11ObjectType::PK11_TypePubKey;
90}
91unsafe impl Pkcs11Object for SymKey {
92    const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType = nss_sys::PK11ObjectType::PK11_TypeSymKey;
93}
94
95// From https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines#Thread_Safety:
96// "Data structures that are read only, like SECKEYPublicKeys or PK11SymKeys, need not be protected."
97unsafe impl Send for PrivateKey {}
98unsafe impl Send for PublicKey {}
99
100impl PrivateKey {
101    pub fn convert_to_public_key(&self) -> Result<PublicKey> {
102        Ok(unsafe { PublicKey::from_ptr(nss_sys::SECKEY_ConvertToPublicKey(self.as_mut_ptr()))? })
103    }
104
105    // To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate
106    // generates a random ID for each key. The given template must contain an
107    // attribute slot for a key ID, but it must consist of a null pointer and have a
108    // length of 0.
109    pub(crate) fn from_private_key_template(
110        mut template: Vec<nss_sys::CK_ATTRIBUTE>,
111    ) -> Result<Self> {
112        // Generate a random 160-bit object ID. This ID must be unique.
113        let mut obj_id_buf = vec![0u8; 160 / 8];
114        generate_random(&mut obj_id_buf)?;
115        let mut obj_id = nss_sys::SECItem {
116            type_: nss_sys::SECItemType::siBuffer as u32,
117            data: obj_id_buf.as_ptr() as *mut c_uchar,
118            len: c_uint::try_from(obj_id_buf.len())?,
119        };
120        let slot = get_internal_slot()?;
121        let mut pre_existing_key = unsafe {
122            nss_sys::PK11_FindKeyByKeyID(slot.as_mut_ptr(), &mut obj_id, std::ptr::null_mut())
123        };
124        if !pre_existing_key.is_null() {
125            // Note that we can't just call SECKEY_DestroyPrivateKey here because that
126            // will destroy the PKCS#11 object that is backing a preexisting key (that
127            // we still have a handle on somewhere else in memory). If that object were
128            // destroyed, cryptographic operations performed by that other key would
129            // fail.
130            unsafe {
131                destroy_private_key_without_destroying_pkcs11_object(pre_existing_key);
132            }
133            // Try again with a new ID (but only once - collisions are very unlikely).
134            generate_random(&mut obj_id_buf)?;
135            pre_existing_key = unsafe {
136                nss_sys::PK11_FindKeyByKeyID(slot.as_mut_ptr(), &mut obj_id, std::ptr::null_mut())
137            };
138            if !pre_existing_key.is_null() {
139                unsafe {
140                    destroy_private_key_without_destroying_pkcs11_object(pre_existing_key);
141                }
142                return Err(ErrorKind::InternalError.into());
143            }
144        }
145        let template_len = c_int::try_from(template.len())?;
146        let id_attr: &mut nss_sys::CK_ATTRIBUTE = template
147            .iter_mut()
148            .find(|&&mut attr| {
149                attr.type_ == (nss_sys::CKA_ID as nss_sys::CK_ATTRIBUTE_TYPE)
150                    && attr.pValue.is_null()
151                    && attr.ulValueLen == 0
152            })
153            .ok_or(ErrorKind::InternalError)?;
154        id_attr.pValue = obj_id_buf.as_mut_ptr() as *mut c_void;
155        id_attr.ulValueLen = nss_sys::CK_ULONG::try_from(obj_id_buf.len())?;
156        // We use `PK11_CreateGenericObject` instead of `PK11_CreateManagedGenericObject`
157        // to leak the reference on purpose because `PK11_FindKeyByKeyID` will take
158        // ownership of it.
159        let _obj = unsafe {
160            GenericObject::from_ptr(nss_sys::PK11_CreateGenericObject(
161                slot.as_mut_ptr(),
162                template.as_mut_ptr(),
163                template_len,
164                nss_sys::PR_FALSE,
165            ))?
166        };
167        // Have NSS translate the object to a private key.
168        Ok(unsafe {
169            PrivateKey::from_ptr(nss_sys::PK11_FindKeyByKeyID(
170                slot.as_mut_ptr(),
171                &mut obj_id,
172                std::ptr::null_mut(),
173            ))?
174        })
175    }
176}
177
178// This is typically used by functions receiving a pointer to an `out SECItem`,
179// where we allocate the struct, but NSS allocates the elements it points to.
180pub(crate) struct ScopedSECItem {
181    wrapped: nss_sys::SECItem,
182}
183
184impl ScopedSECItem {
185    pub(crate) fn empty(r#type: nss_sys::SECItemType) -> Self {
186        ScopedSECItem {
187            wrapped: nss_sys::SECItem {
188                type_: r#type as u32,
189                data: ptr::null_mut(),
190                len: 0,
191            },
192        }
193    }
194
195    pub(crate) fn as_mut_ref(&mut self) -> &mut nss_sys::SECItem {
196        &mut self.wrapped
197    }
198}
199
200impl Deref for ScopedSECItem {
201    type Target = nss_sys::SECItem;
202    #[inline]
203    fn deref(&self) -> &nss_sys::SECItem {
204        &self.wrapped
205    }
206}
207
208impl Drop for ScopedSECItem {
209    fn drop(&mut self) {
210        unsafe {
211            // PR_FALSE asks the NSS allocator not to free the SECItem
212            // itself, and just the pointee of `self.wrapped.data`.
213            nss_sys::SECITEM_FreeItem(&mut self.wrapped, nss_sys::PR_FALSE);
214        }
215    }
216}
217
218// This helper function will release the memory backing a SECKEYPrivateKey and
219// any resources acquired in its creation. It will leave the backing PKCS#11
220// object untouched, however. This should only be called from
221// PrivateKeyFromPrivateKeyTemplate.
222// From: https://searchfox.org/mozilla-central/rev/444ee13e14fe30451651c0f62b3979c76766ada4/dom/crypto/CryptoKey.cpp#80
223unsafe fn destroy_private_key_without_destroying_pkcs11_object(
224    key: *mut nss_sys::SECKEYPrivateKey,
225) {
226    assert!(!key.is_null());
227    nss_sys::PK11_FreeSlot((*key).pkcs11Slot);
228    nss_sys::PORT_FreeArena((*key).arena, nss_sys::PR_TRUE);
229}