vrf_mod/ecvrf/
primitives.rs

1//! Data conversion primitives
2//!
3
4use openssl::{
5    bn::{BigNum, BigNumContext},
6    error::ErrorStack,
7};
8
9/// Converts a sequence of blen bits to a non-negative integer that is less than 2^qlen as specified in
10/// [Section 2.3.2 of \[RFC6979\]](https://www.rfc-editor.org/rfc/rfc6979#section-2.3.2)
11///
12/// # Arguments
13///
14/// * `num`: an octet slice representing the number to be converted
15/// * `qlen`: the length of the output `BigNum`
16///
17/// # Returns:
18///
19/// * a `BigNum` representing the conversion.
20///
21pub fn bits2ints(
22    num: &[u8],
23    qlen: usize,
24) -> Result<BigNum, ErrorStack> {
25    let vlen = num.len() * 8;
26    let result = BigNum::from_slice(num)?;
27
28    if vlen > qlen {
29        let mut truncated = BigNum::new()?;
30        truncated.rshift(&result, (vlen - qlen) as i32)?;
31        
32        Ok(truncated)
33    } else {
34        Ok(result)
35    }
36}
37
38/// Converts a sequence of blen bits to an output of rlen bits as specified in
39/// [Section 2.3.4 of \[RFC6979\]](https://www.rfc-editor.org/rfc/rfc6979#section-2.3.4)
40///
41/// # Arguments
42///
43/// * `num`: an octet slice
44/// * `length`: transform input `num` to a sequence of `length`
45/// * `order`: right output boundary non-inclusive, output lies in range = (0, order)
46/// * `bn_ctx`: `BigNumContext` for arithmetic
47///
48/// # Returns:
49///
50/// * a vector of octets
51///
52pub fn bits2octets(
53    num: &[u8],
54    length: usize,
55    order: &BigNum,
56    bn_ctx: &mut BigNumContext,
57) -> Result<Vec<u8>, ErrorStack> {
58    let z1 = bits2ints(num, length)?;
59    let z2 = BigNum::new().and_then(|mut result| {
60        result.nnmod(&z1, order, bn_ctx)?;
61        Ok(result.to_vec()) 
62    })?;
63    Ok(z2)
64}
65
66/// Appends zeroes if provided slice is smaller than given length in bits
67///
68/// # Arguments
69/// 
70/// * `data`: octet slice
71/// * `length`: size in bits after appending zeroes
72///
73/// # Returns: 
74///
75/// * a vector of octets with leading zeroes (if necessary) 
76///
77pub fn append_zeroes(
78    data: &[u8],
79    length: usize,
80) -> Vec<u8> {
81    // Check if data length does not exceed provided transform length
82    if data.len() * 8 > length {
83        return data.to_vec();
84    }
85
86    let zeroes = if length % 8 > 0 {
87        vec![0; length / 8 - data.len() + 1]
88    } else {
89        vec![0; length / 8 - data.len()]
90    };
91
92    [&zeroes.as_slice(), data].concat()
93}
94
95#[cfg(test)]
96mod test {
97    use super::*;
98
99    #[test]
100    fn test_bits2ints() {
101        let octets = &[0x01; 33];
102        let data = BigNum::from_slice(octets).unwrap();
103    
104        let result = bits2ints(octets, 256).unwrap();
105        let mut expected_result = BigNum::new().unwrap();
106        expected_result.rshift(&data, 8).unwrap();
107
108        assert_eq!(result.to_vec(), expected_result.to_vec());
109    }
110
111    #[test]
112    fn test_bits2octets() {
113        // hex-encoded -> 'this is a sample string'
114        let data = hex::decode("7468697320697320612073616d706c65206d657373616765")
115            .unwrap();
116        let order_ = hex::decode("020000000000000000000b2e3b3a5f7182d417ceba").unwrap();
117        let order = BigNum::from_slice(&order_.as_slice()).unwrap();
118        let mut bn_ctx = BigNumContext::new().unwrap();
119        let result = bits2octets(
120            &data.as_slice(),
121            order.num_bits() as usize,
122            &order,
123            &mut bn_ctx,
124        )
125        .unwrap();
126
127        let expected_result = [
128            1, 209, 161, 165, 204, 129, 165, 204, 129, 132, 
129            129, 205, 133, 181, 193, 177, 148, 129, 181, 149, 205
130        ];
131
132        assert_eq!(result, expected_result);
133    }
134}