summaryrefslogtreecommitdiffstats
path: root/third_party/rust/authenticator/src/crypto/der.rs
blob: 39c5e0b676b0f96a58b0c563d810e7f509e772d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use super::CryptoError;

pub const TAG_INTEGER: u8 = 0x02;
pub const TAG_BIT_STRING: u8 = 0x03;
#[cfg(all(test, feature = "crypto_nss"))]
pub const TAG_OCTET_STRING: u8 = 0x04;
pub const TAG_NULL: u8 = 0x05;
pub const TAG_OBJECT_ID: u8 = 0x06;
pub const TAG_SEQUENCE: u8 = 0x30;

// Object identifiers in DER tag-length-value form
pub const OID_EC_PUBLIC_KEY_BYTES: &[u8] = &[
    /* RFC 5480 (id-ecPublicKey) */
    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
];
pub const OID_SECP256R1_BYTES: &[u8] = &[
    /* RFC 5480 (secp256r1) */
    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
];
pub const OID_ED25519_BYTES: &[u8] = &[/* RFC 8410 (id-ed25519) */ 0x2b, 0x65, 0x70];
pub const OID_RS256_BYTES: &[u8] = &[
    /* RFC 4055 (sha256WithRSAEncryption) */
    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
];

pub type Result<T> = std::result::Result<T, CryptoError>;

const MAX_TAG_AND_LENGTH_BYTES: usize = 4;
fn write_tag_and_length(out: &mut Vec<u8>, tag: u8, len: usize) -> Result<()> {
    if len > 0xFFFF {
        return Err(CryptoError::LibraryFailure);
    }
    out.push(tag);
    if len > 0xFF {
        out.push(0x82);
        out.push((len >> 8) as u8);
        out.push(len as u8);
    } else if len > 0x7F {
        out.push(0x81);
        out.push(len as u8);
    } else {
        out.push(len as u8);
    }
    Ok(())
}

pub fn integer(val: &[u8]) -> Result<Vec<u8>> {
    if val.is_empty() {
        return Err(CryptoError::MalformedInput);
    }
    // trim leading zeros, leaving a single zero if the input is the zero vector.
    let mut val = val;
    while val.len() > 1 && val[0] == 0 {
        val = &val[1..];
    }
    let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + 1 + val.len());
    if val[0] & 0x80 != 0 {
        // needs zero prefix
        write_tag_and_length(&mut out, TAG_INTEGER, 1 + val.len())?;
        out.push(0x00);
        out.extend_from_slice(val);
    } else {
        write_tag_and_length(&mut out, TAG_INTEGER, val.len())?;
        out.extend_from_slice(val);
    }
    Ok(out)
}

pub fn bit_string(val: &[u8]) -> Result<Vec<u8>> {
    let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + 1 + val.len());
    write_tag_and_length(&mut out, TAG_BIT_STRING, 1 + val.len())?;
    out.push(0x00); // trailing bits aren't supported
    out.extend_from_slice(val);
    Ok(out)
}

pub fn null() -> Result<Vec<u8>> {
    let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES);
    write_tag_and_length(&mut out, TAG_NULL, 0)?;
    Ok(out)
}

pub fn object_id(val: &[u8]) -> Result<Vec<u8>> {
    let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + val.len());
    write_tag_and_length(&mut out, TAG_OBJECT_ID, val.len())?;
    out.extend_from_slice(val);
    Ok(out)
}

pub fn sequence(items: &[&[u8]]) -> Result<Vec<u8>> {
    let len = items.iter().map(|i| i.len()).sum();
    let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + len);
    write_tag_and_length(&mut out, TAG_SEQUENCE, len)?;
    for item in items {
        out.extend_from_slice(item);
    }
    Ok(out)
}

#[cfg(all(test, feature = "crypto_nss"))]
pub fn octet_string(val: &[u8]) -> Result<Vec<u8>> {
    let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + val.len());
    write_tag_and_length(&mut out, TAG_OCTET_STRING, val.len())?;
    out.extend_from_slice(val);
    Ok(out)
}

#[cfg(all(test, feature = "crypto_nss"))]
pub fn context_specific_explicit_tag(tag: u8, content: &[u8]) -> Result<Vec<u8>> {
    let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + content.len());
    write_tag_and_length(&mut out, 0xa0 + tag, content.len())?;
    out.extend_from_slice(content);
    Ok(out)
}

// Given "tag || len || value || rest" where tag and len are of length one, len is in [0, 127],
// and value is of length len, returns (value, rest)
#[cfg(all(test, feature = "crypto_nss"))]
fn expect_tag_with_short_len(tag: u8, z: &[u8]) -> Result<(&[u8], &[u8])> {
    if z.is_empty() {
        return Err(CryptoError::MalformedInput);
    }
    let (h, z) = z.split_at(1);
    if h[0] != tag || z.is_empty() {
        return Err(CryptoError::MalformedInput);
    }
    let (h, z) = z.split_at(1);
    if h[0] >= 0x80 || h[0] as usize > z.len() {
        return Err(CryptoError::MalformedInput);
    }
    Ok(z.split_at(h[0] as usize))
}

// Given a DER encoded RFC 3279 Ecdsa-Sig-Value,
//   Ecdsa-Sig-Value  ::=  SEQUENCE  {
//        r     INTEGER,
//        s     INTEGER },
// with r and s < 2^256, returns a 64 byte array containing
// r and s encoded as 32 byte zero-padded big endian unsigned
// integers
#[cfg(all(test, feature = "crypto_nss"))]
pub fn read_p256_sig(z: &[u8]) -> Result<Vec<u8>> {
    // Strip the tag and length.
    let (z, rest) = expect_tag_with_short_len(TAG_SEQUENCE, z)?;

    // The input should not have any trailing data.
    if !rest.is_empty() {
        return Err(CryptoError::MalformedInput);
    }

    let read_u256 = |z| -> Result<(&[u8], &[u8])> {
        let (r, z) = expect_tag_with_short_len(TAG_INTEGER, z)?;
        // We're expecting r < 2^256, so no more than 33 bytes as a signed integer.
        if r.is_empty() || r.len() > 33 {
            return Err(CryptoError::MalformedInput);
        }
        // If it is 33 bytes the leading byte must be zero.
        if r.len() == 33 && r[0] != 0 {
            return Err(CryptoError::MalformedInput);
        }
        // Ensure r is no more than 32 bytes.
        if r.len() == 33 {
            Ok((&r[1..], z))
        } else {
            Ok((r, z))
        }
    };

    let (r, z) = read_u256(z)?;
    let (s, z) = read_u256(z)?;

    // We should have consumed the entire buffer
    if !z.is_empty() {
        return Err(CryptoError::MalformedInput);
    }

    // Left pad each integer with zeros to length 32 and concatenate the results
    let mut out = vec![0u8; 64];
    {
        let (r_out, s_out) = out.split_at_mut(32);
        r_out[32 - r.len()..].copy_from_slice(r);
        s_out[32 - s.len()..].copy_from_slice(s);
    }
    Ok(out)
}