• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

namib-project / dcaf-rs / 11935120896

20 Nov 2024 02:11PM UTC coverage: 86.555% (+1.3%) from 85.242%
11935120896

Pull #27

github

web-flow
Merge d2b3d706b into 383248641
Pull Request #27: ci: update grcov to latest stable version

6116 of 7066 relevant lines covered (86.56%)

167.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.48
/src/token/cose/util/header.rs
1
/*
2
 * Copyright (c) 2024 The NAMIB Project Developers.
3
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6
 * option. This file may not be copied, modified, or distributed
7
 * except according to those terms.
8
 *
9
 * SPDX-License-Identifier: MIT OR Apache-2.0
10
 */
11
use alloc::borrow::ToOwned;
12
use alloc::collections::BTreeSet;
13
use alloc::vec::Vec;
14
use core::fmt::Display;
15

16
use alloc::borrow::Borrow;
17
use coset::iana::EnumI64;
18
use coset::{iana, Algorithm, CoseKey, Header, KeyOperation, Label};
19

20
use crate::error::CoseCipherError;
21
use crate::token::cose::key::KeyProvider;
22
/// Returns the set of header parameters that are set in the given `header_bucket`.
23
fn create_header_parameter_set(header_bucket: &Header) -> BTreeSet<Label> {
8,624✔
24
    let mut header_bucket_fields = BTreeSet::new();
8,624✔
25

8,624✔
26
    if header_bucket.alg.is_some() {
8,624✔
27
        header_bucket_fields.insert(Label::Int(iana::HeaderParameter::Alg.to_i64()));
4,068✔
28
    }
4,560✔
29
    if header_bucket.content_type.is_some() {
8,624✔
30
        header_bucket_fields.insert(Label::Int(iana::HeaderParameter::ContentType.to_i64()));
49✔
31
    }
8,575✔
32
    if !header_bucket.key_id.is_empty() {
8,624✔
33
        header_bucket_fields.insert(Label::Int(iana::HeaderParameter::Kid.to_i64()));
1,392✔
34
    }
7,232✔
35
    if !header_bucket.crit.is_empty() {
8,624✔
36
        header_bucket_fields.insert(Label::Int(iana::HeaderParameter::Crit.to_i64()));
×
37
    }
8,624✔
38
    if !header_bucket.counter_signatures.is_empty() {
8,624✔
39
        header_bucket_fields.insert(Label::Int(iana::HeaderParameter::CounterSignature.to_i64()));
×
40
    }
8,624✔
41
    if !header_bucket.iv.is_empty() {
8,624✔
42
        header_bucket_fields.insert(Label::Int(iana::HeaderParameter::Iv.to_i64()));
1,369✔
43
    }
7,255✔
44
    if !header_bucket.partial_iv.is_empty() {
8,624✔
45
        header_bucket_fields.insert(Label::Int(iana::HeaderParameter::PartialIv.to_i64()));
6✔
46
    }
8,618✔
47

48
    header_bucket_fields.extend(header_bucket.rest.iter().map(|(k, _v)| k.clone()));
8,624✔
49

8,624✔
50
    header_bucket_fields
8,624✔
51
}
8,624✔
52

53
/// Ensures that there are no duplicate headers in the `protected_header` and `unprotected_header`
54
/// buckets.
55
pub(crate) fn check_for_duplicate_headers<E: Display>(
1,957✔
56
    protected_header: &Header,
1,957✔
57
    unprotected_header: &Header,
1,957✔
58
) -> Result<(), CoseCipherError<E>> {
1,957✔
59
    let unprotected_header_set = create_header_parameter_set(unprotected_header);
1,957✔
60
    let protected_header_set = create_header_parameter_set(protected_header);
1,957✔
61
    let duplicate_header_fields: Vec<&Label> = unprotected_header_set
1,957✔
62
        .intersection(&protected_header_set)
1,957✔
63
        .collect();
1,957✔
64
    if duplicate_header_fields.is_empty() {
1,957✔
65
        Ok(())
1,912✔
66
    } else {
67
        Err(CoseCipherError::DuplicateHeaders(
45✔
68
            duplicate_header_fields.into_iter().cloned().collect(),
45✔
69
        ))
45✔
70
    }
71
}
1,957✔
72

73
/// Determines the value of a header param based on the provided `protected` and `unprotected`
74
/// header buckets and the `accessor` function that determines the header parameter from a header
75
/// reference.
76
pub(crate) fn determine_header_param<'a, F: Fn(&'a Header) -> Option<T>, T: 'a>(
4,665✔
77
    protected_header: Option<&'a Header>,
4,665✔
78
    unprotected_header: Option<&'a Header>,
4,665✔
79
    accessor: F,
4,665✔
80
) -> Option<T> {
4,665✔
81
    protected_header
4,665✔
82
        .into_iter()
4,665✔
83
        .chain(unprotected_header)
4,665✔
84
        .find_map(accessor)
4,665✔
85
}
4,665✔
86

87
/// Determines the algorithm to use for the signing operation based on the supplied key and headers.
88
pub(crate) fn determine_algorithm<CE: Display>(
3,384✔
89
    parsed_key: Option<&CoseKey>,
3,384✔
90
    protected_header: Option<&Header>,
3,384✔
91
    unprotected_header: Option<&Header>,
3,384✔
92
) -> Result<iana::Algorithm, CoseCipherError<CE>> {
3,384✔
93
    let alg = determine_header_param(protected_header, unprotected_header, |h| h.alg.clone())
4,987✔
94
        .or_else(|| parsed_key.and_then(|k| k.alg.clone()))
3,384✔
95
        .ok_or(CoseCipherError::NoAlgorithmDeterminable)?;
3,384✔
96

97
    if let Algorithm::Assigned(alg) = alg {
3,349✔
98
        Ok(alg)
3,337✔
99
    } else {
100
        Err(CoseCipherError::UnsupportedAlgorithm(alg))
12✔
101
    }
102
}
3,384✔
103

104
/// Queries the key provider for keys and checks for each returned key whether it is a possible
105
/// candidate for the operation and algorithm.
106
///
107
/// Returns an iterator that for each key returned by the key provider either returns the key plus
108
/// algorithm to use or the corresponding error that describes the reason why this key is not
109
/// suitable.
110
///
111
/// This function performs the algorithm-independent checks for whether a key is a suitable
112
/// candidate, but not any algorithm-specific checks (e.g. required key parameters, key length,
113
/// etc.). Those will have to be checked by the caller.
114
pub(crate) fn determine_key_candidates<'a, CKP: KeyProvider, CE: Display>(
1,701✔
115
    key_provider: &'a CKP,
1,701✔
116
    protected: Option<&'a Header>,
1,701✔
117
    unprotected: Option<&'a Header>,
1,701✔
118
    operation: BTreeSet<KeyOperation>,
1,701✔
119
) -> impl Iterator<Item = Result<(CoseKey, iana::Algorithm), (CoseKey, CoseCipherError<CE>)>> + 'a {
1,701✔
120
    let key_id = protected
1,701✔
121
        .map(|v| v.key_id.as_slice())
1,701✔
122
        .filter(|v| !v.is_empty())
1,701✔
123
        .or_else(|| unprotected.map(|v| v.key_id.as_slice()))
1,701✔
124
        .filter(|v| !v.is_empty());
1,701✔
125

1,701✔
126
    key_provider.lookup_key(key_id).map(move |k| {
1,701✔
127
        let k_borrow: &CoseKey = k.borrow();
1,699✔
128
        if !k_borrow.key_ops.is_empty()
1,699✔
129
            && k_borrow.key_ops.intersection(&operation).next().is_some()
×
130
        {
131
            return Err((
×
132
                k_borrow.clone(),
×
133
                CoseCipherError::KeyOperationNotPermitted(
×
134
                    k_borrow.key_ops.clone(),
×
135
                    operation.clone(),
×
136
                ),
×
137
            ));
×
138
        }
1,699✔
139
        let chosen_alg = determine_algorithm(Some(k_borrow), protected, unprotected)
1,699✔
140
            .map_err(|e| (k_borrow.clone(), e))?;
1,699✔
141
        if let Some(key_alg) = k_borrow.alg.as_ref() {
1,665✔
142
            if Algorithm::Assigned(chosen_alg) != *key_alg {
163✔
143
                return Err((
18✔
144
                    k_borrow.clone(),
18✔
145
                    CoseCipherError::KeyAlgorithmMismatch(
18✔
146
                        key_alg.clone(),
18✔
147
                        Algorithm::Assigned(chosen_alg),
18✔
148
                    ),
18✔
149
                ));
18✔
150
            }
145✔
151
        }
1,502✔
152
        Ok((k_borrow.to_owned(), chosen_alg))
1,647✔
153
    })
1,701✔
154
}
1,701✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc