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

Make sqlite database upgradabale and crypto-agile #124

Merged
merged 6 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ ecdh = ["ecc"]
eddsa = ["ecc"]
ec_montgomery = ["ecc"]
hash = []
hkdf = []
hkdf = ["hmac"]
hmac = ["hash"]
pbkdf2 = ["hmac"]
rsa = []
Expand All @@ -64,8 +64,8 @@ tlskdf = []

# Databases
jsondb = ["memorydb"]
memorydb = ["aes", "pbkdf2"] # for encryption
sqlitedb = [ "dep:rusqlite", "aes", "hmac", "pbkdf2" ]
memorydb = ["aes", "hkdf", "pbkdf2"] # for encryption
sqlitedb = [ "dep:rusqlite", "aes", "hkdf", "pbkdf2" ]
nssdb = [ "dep:rusqlite", "aes", "hmac", "pbkdf2" ]

default = [ "sqlitedb" ]
Expand Down
113 changes: 113 additions & 0 deletions doc/storage-encryption.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
Storage Encryption
------------------

A PKCS#11 token uses a storage component to save keys.
In the case of a software token, it is a good idea to encrypt sensible
information in the storage so that keys cannot be easily stolen or tampered
with.

Kryoptic adopts a flexible and agile encryption system that allows for future
changes while maintaining reasonable comaptibility.

The aim of the v1 scheme identified here is to provide reasonable security
while allowing simplified operations.

The token allows for two PINs to be used, the SO PIN and the USER PIN.
The SO PIN is considered a recvoery PIN. Both PINs allow for access to the
key material.

The PINs are used in conjunction with PBKDF2 to derive a KEK that is used to
decrypt the storage Encryption key. The Encryption key is versioned and is used
to encrypt each secret attribute individually by using a key derivation step to
obtain an object specific key. The Key Derivation algorithms and paramters are
flexibly defined by wrapping the keys and encrypted values using PKCS#5 like
ASN.1 structures.

This OID has been defined to identify the HKDF-Expand Key Derivation function:
1.3.6.1.4.1.2312.20.1.1

KKBPS - Kryoptic Key Based Protection Scheme v1
simo5 marked this conversation as resolved.
Show resolved Hide resolved
===============================================

The object identifier id-KKBPS1 identifies this scheme: 1.3.6.1.4.1.2312.20.2.1

This is the structure used to define all the parameters and identify the keys
used to encrypt or integrity protect keys or other secrets.

For object encryption the Key derivation is a simple HKDF-Expand operation that
uses a key generated via a cryptographic RNG.

HKDF-Expand(RK, info, L) -> EK

where:
- RK: randomly generated key
- info: a string uniquely identifying the object carrying the attribute
- L: the length of the derived EK
- EK: The key used for the actual encryption

Encryption is performed using AES-GCM

AES-GCM(EK, IV, AAD, data) -> ED, TAG

where:
- EK: The Key generated by the Key derivation Step
- IV: A nonce generated via a cryptographic RNG
- AAD: The attribute type represented as a little endian u32
- data: The data to be protected
- ED: The encrypted data
- TAG: The authentication tag (64bit in length)

The standard PBKDF2 to derive the encryption key used to protect the Key
Encryption Key, also referenced as RK above.

The ASN.1 structure used to encode the parameters:

KKBPS1-params ::= SEQUENCE {
keyVersionNumber INTEGER,
keyDerivationFunc AlgorithmIdentifier {{KKBPS1-KDFs}},
encryptionScheme AlgorithmIdentifier {{KKBPS1-Encs}}
}

KKBPS1-KDFs ALGORITHM-IDENTIFIER ::= {
{KKDF1-params IDENTIFIED BY id-KKDF1},
{PBKDF2-params IDENTIFIED BY id-PBKDF2},
...
}

KKDF1-params ::= SEQUENCE {
prf AlgorithmIdentifier
info OCTET STRING,
keyLength INTEGER (16 | 24 | 32),
}

KKBPS1-Encs ALGORITHM-IDENTIFIER ::= {
{aes128gcm IDENTIFIED BY id-aes128-GCM},
{aes192gcm IDENTIFIED BY id-aes192-GCM},
{aes256gcm IDENTIFIED BY id-aes256-GCM},
...
}

The GCM Paramters are defined as:

KGCMParameters::= SEQUENCE {
aesIv OCTET STRING (SIZE(12)),
aesTag OCTET STRING (SIZE(8)),
}

Kryoptic Protected Data
=======================

This structure encapsulates the data and all the parameters needed for
protection, whether that is encryption/decryption or just integrity tags.

It is defined as:

KPD1 ::= SEQUENCE {
algorithm AlgorithmIdentifier {
{KKBPS1-params IDENTIFIED BY id-KKBPS1}
},
data OCTET STRING,
signature OCTET STRING OPTIONAL
}


6 changes: 1 addition & 5 deletions src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ macro_rules! attrmap_element {
};
}

static ATTRMAP: [Attrmap<'_>; 147] = [
static ATTRMAP: [Attrmap<'_>; 143] = [
attrmap_element!(CKA_CLASS; as NumType),
attrmap_element!(CKA_TOKEN; as BoolType),
attrmap_element!(CKA_PRIVATE; as BoolType),
Expand Down Expand Up @@ -169,10 +169,6 @@ static ATTRMAP: [Attrmap<'_>; 147] = [
attrmap_element!(CKA_HSS_KEYS_REMAINING; as NumType),
attrmap_element!(KRA_MAX_LOGIN_ATTEMPTS; as NumType),
attrmap_element!(KRA_LOGIN_ATTEMPTS; as NumType),
attrmap_element!(KRA_FLAGS; as NumType),
attrmap_element!(KRA_MANUFACTURER_ID; as StringType),
attrmap_element!(KRA_MODEL; as StringType),
attrmap_element!(KRA_SERIAL_NUMBER; as StringType),
attrmap_element!(CKA_VALIDATION_TYPE; as NumType),
attrmap_element!(CKA_VALIDATION_VERSION; as BytesType),
attrmap_element!(CKA_VALIDATION_LEVEL; as NumType),
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ impl From<asn1::WriteError> for Error {
}
}

impl From<Vec<u8>> for Error {
fn from(v: Vec<u8>) -> Error {
Error::buf_too_small(v.len())
}
}

#[macro_export]
macro_rules! some_or_err {
($action:expr) => {
Expand Down
82 changes: 82 additions & 0 deletions src/kasn1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,88 @@ impl PrivateKeyInfo<'_> {
}
}

/* Following structs are used for storage purposes */
#[derive(
asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone, Eq, Debug,
)]
pub struct KAlgorithmIdentifier<'a> {
pub oid: asn1::DefinedByMarker<asn1::ObjectIdentifier>,
#[defined_by(oid)]
pub params: KAlgorithmParameters<'a>,
}

#[derive(
asn1::Asn1DefinedByRead,
asn1::Asn1DefinedByWrite,
PartialEq,
Eq,
Hash,
Clone,
Debug,
)]
pub enum KAlgorithmParameters<'a> {
#[defined_by(oid::KKDF1_OID)]
Kkdf1(KKDF1Params<'a>),

#[defined_by(oid::PBKDF2_OID)]
Pbkdf2(pkcs::PBKDF2Params<'a>),

#[defined_by(oid::KKBPS1_OID)]
Kkbps1(KKBPS1Params<'a>),

#[defined_by(oid::AES_128_GCM_OID)]
Aes128Gcm(KGCMParams),
#[defined_by(oid::AES_192_GCM_OID)]
Aes192Gcm(KGCMParams),
#[defined_by(oid::AES_256_GCM_OID)]
Aes256Gcm(KGCMParams),

#[defined_by(oid::HMAC_WITH_SHA256_OID)]
HmacWithSha256(Option<asn1::Null>),
#[defined_by(oid::HMAC_WITH_SHA384_OID)]
HmacWithSha384(Option<asn1::Null>),
#[defined_by(oid::HMAC_WITH_SHA512_OID)]
HmacWithSha512(Option<asn1::Null>),

#[default]
Other(asn1::ObjectIdentifier, Option<asn1::Tlv<'a>>),
}

#[derive(
asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone, Debug,
)]
pub struct KKDF1Params<'a> {
pub prf: Box<KAlgorithmIdentifier<'a>>,
pub info: &'a [u8],
pub key_length: u64,
}

#[derive(
asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone, Debug,
)]
pub struct KKBPS1Params<'a> {
pub key_version_number: u64,
pub key_derivation_func: Box<KAlgorithmIdentifier<'a>>,
pub encryption_scheme: Box<KAlgorithmIdentifier<'a>>,
}

#[derive(
asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone, Debug,
)]
pub struct KProtectedData<'a> {
pub algorithm: Box<KAlgorithmIdentifier<'a>>,
pub data: &'a [u8],
pub signature: Option<&'a [u8]>,
}

#[derive(
asn1::Asn1Read, asn1::Asn1Write, PartialEq, Eq, Hash, Clone, Debug,
)]
pub struct KGCMParams {
pub aes_iv: [u8; 12],
pub aes_tag: [u8; 8],
}

#[allow(dead_code)]
pub mod oid;

Expand Down
17 changes: 17 additions & 0 deletions src/kasn1/oid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,20 @@ include! {"pyca/oid.rs"}

pub const PBMAC1_OID: asn1::ObjectIdentifier =
asn1::oid!(1, 2, 840, 113549, 1, 5, 14);

pub const AES_128_GCM_OID: asn1::ObjectIdentifier =
asn1::oid!(2, 16, 840, 1, 101, 3, 4, 1, 6);
pub const AES_192_GCM_OID: asn1::ObjectIdentifier =
asn1::oid!(2, 16, 840, 1, 101, 3, 4, 1, 26);
pub const AES_256_GCM_OID: asn1::ObjectIdentifier =
asn1::oid!(2, 16, 840, 1, 101, 3, 4, 1, 46);

pub const HMAC_WITH_SHA384_OID: asn1::ObjectIdentifier =
asn1::oid!(1, 2, 840, 113549, 2, 10);
pub const HMAC_WITH_SHA512_OID: asn1::ObjectIdentifier =
asn1::oid!(1, 2, 840, 113549, 2, 11);

pub const KKDF1_OID: asn1::ObjectIdentifier =
asn1::oid!(1, 3, 6, 1, 4, 1, 2312, 20, 1, 1);
pub const KKBPS1_OID: asn1::ObjectIdentifier =
asn1::oid!(1, 3, 6, 1, 4, 1, 2312, 20, 2, 1);
4 changes: 0 additions & 4 deletions src/pkcs11/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ pub const KRO_TOKEN_DATA: CK_OBJECT_CLASS = KRY_VENDOR_OFFSET + 1;
/* Attributes */
pub const KRA_MAX_LOGIN_ATTEMPTS: CK_ATTRIBUTE_TYPE = KRY_VENDOR_OFFSET + 1;
pub const KRA_LOGIN_ATTEMPTS: CK_ATTRIBUTE_TYPE = KRY_VENDOR_OFFSET + 2;
pub const KRA_FLAGS: CK_ATTRIBUTE_TYPE = KRY_VENDOR_OFFSET + 3;
pub const KRA_MANUFACTURER_ID: CK_ATTRIBUTE_TYPE = KRY_VENDOR_OFFSET + 4;
pub const KRA_MODEL: CK_ATTRIBUTE_TYPE = KRY_VENDOR_OFFSET + 5;
pub const KRA_SERIAL_NUMBER: CK_ATTRIBUTE_TYPE = KRY_VENDOR_OFFSET + 6;
/* + 10 taken by pkcs11/validation_draft.rs */

/* Errors */
Expand Down
Loading