Skip to content

Commit 4eab8e2

Browse files
committed
Add der_spki() methods for all supported key types
1 parent 0c72cca commit 4eab8e2

File tree

3 files changed

+490
-288
lines changed

3 files changed

+490
-288
lines changed

src/crypto/der.rs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use super::CryptoError;
2+
3+
pub const TAG_INTEGER: u8 = 0x02;
4+
pub const TAG_BIT_STRING: u8 = 0x03;
5+
#[cfg(all(test, feature = "crypto_nss"))]
6+
pub const TAG_OCTET_STRING: u8 = 0x04;
7+
pub const TAG_NULL: u8 = 0x05;
8+
pub const TAG_OBJECT_ID: u8 = 0x06;
9+
pub const TAG_SEQUENCE: u8 = 0x30;
10+
11+
// Object identifiers in DER tag-length-value form
12+
pub const OID_EC_PUBLIC_KEY_BYTES: &[u8] = &[
13+
/* RFC 5480 (id-ecPublicKey) */
14+
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
15+
];
16+
pub const OID_SECP256R1_BYTES: &[u8] = &[
17+
/* RFC 5480 (secp256r1) */
18+
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
19+
];
20+
pub const OID_ED25519_BYTES: &[u8] = &[/* RFC 8410 (id-ed25519) */ 0x2b, 0x65, 0x70];
21+
pub const OID_RS256_BYTES: &[u8] = &[
22+
/* RFC 4055 (sha256WithRSAEncryption) */
23+
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
24+
];
25+
26+
pub type Result<T> = std::result::Result<T, CryptoError>;
27+
28+
const MAX_TAG_AND_LENGTH_BYTES: usize = 4;
29+
fn write_tag_and_length(out: &mut Vec<u8>, tag: u8, len: usize) -> Result<()> {
30+
if len > 0xFFFF {
31+
return Err(CryptoError::LibraryFailure);
32+
}
33+
out.push(tag);
34+
if len > 0xFF {
35+
out.push(0x82);
36+
out.push((len >> 8) as u8);
37+
out.push(len as u8);
38+
} else if len > 0x7F {
39+
out.push(0x81);
40+
out.push(len as u8);
41+
} else {
42+
out.push(len as u8);
43+
}
44+
Ok(())
45+
}
46+
47+
pub fn integer(val: &[u8]) -> Result<Vec<u8>> {
48+
// trim leading zeros, leaving a single zero if the input is the zero vector.
49+
let mut val = val;
50+
while val.len() > 1 && val[0] == 0 {
51+
val = &val[1..];
52+
}
53+
54+
let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + 1 + val.len());
55+
if !val.is_empty() && val[0] & 0x80 != 0 {
56+
// needs zero prefix
57+
write_tag_and_length(&mut out, TAG_INTEGER, 1 + val.len())?;
58+
out.push(0x00);
59+
out.extend_from_slice(val);
60+
} else {
61+
write_tag_and_length(&mut out, TAG_INTEGER, val.len())?;
62+
out.extend_from_slice(val);
63+
}
64+
Ok(out)
65+
}
66+
67+
pub fn bit_string(val: &[u8]) -> Result<Vec<u8>> {
68+
let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + 1 + val.len());
69+
write_tag_and_length(&mut out, TAG_BIT_STRING, 1 + val.len())?;
70+
out.push(0x00); // trailing bits aren't supported
71+
out.extend_from_slice(val);
72+
Ok(out)
73+
}
74+
75+
pub fn null() -> Result<Vec<u8>> {
76+
let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES);
77+
write_tag_and_length(&mut out, TAG_NULL, 0)?;
78+
Ok(out)
79+
}
80+
81+
pub fn object_id(val: &[u8]) -> Result<Vec<u8>> {
82+
let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + val.len());
83+
write_tag_and_length(&mut out, TAG_OBJECT_ID, val.len())?;
84+
out.extend_from_slice(val);
85+
Ok(out)
86+
}
87+
88+
pub fn sequence(items: &[&[u8]]) -> Result<Vec<u8>> {
89+
let len = items.iter().map(|i| i.len()).sum();
90+
let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + len);
91+
write_tag_and_length(&mut out, TAG_SEQUENCE, len)?;
92+
for item in items {
93+
out.extend_from_slice(item);
94+
}
95+
Ok(out)
96+
}
97+
98+
#[cfg(all(test, feature = "crypto_nss"))]
99+
pub fn octet_string(val: &[u8]) -> Result<Vec<u8>> {
100+
let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + val.len());
101+
write_tag_and_length(&mut out, TAG_OCTET_STRING, val.len())?;
102+
out.extend_from_slice(val);
103+
Ok(out)
104+
}
105+
106+
#[cfg(all(test, feature = "crypto_nss"))]
107+
pub fn context_specific_explicit_tag(tag: u8, content: &[u8]) -> Result<Vec<u8>> {
108+
let mut out = Vec::with_capacity(MAX_TAG_AND_LENGTH_BYTES + content.len());
109+
write_tag_and_length(&mut out, 0xa0 + tag, content.len())?;
110+
out.extend_from_slice(content);
111+
Ok(out)
112+
}
113+
114+
// Given "tag || len || value || rest" where tag and len are of length one, len is in [0, 127],
115+
// and value is of length len, returns (value, rest)
116+
#[cfg(all(test, feature = "crypto_nss"))]
117+
fn expect_tag_with_short_len(tag: u8, z: &[u8]) -> Result<(&[u8], &[u8])> {
118+
if z.is_empty() {
119+
return Err(CryptoError::MalformedInput);
120+
}
121+
let (h, z) = z.split_at(1);
122+
if h[0] != tag || z.is_empty() {
123+
return Err(CryptoError::MalformedInput);
124+
}
125+
let (h, z) = z.split_at(1);
126+
if h[0] >= 0x80 || h[0] as usize > z.len() {
127+
return Err(CryptoError::MalformedInput);
128+
}
129+
Ok(z.split_at(h[0] as usize))
130+
}
131+
132+
// Given a DER encoded RFC 3279 Ecdsa-Sig-Value,
133+
// Ecdsa-Sig-Value ::= SEQUENCE {
134+
// r INTEGER,
135+
// s INTEGER },
136+
// with r and s < 2^256, returns a 64 byte array containing
137+
// r and s encoded as 32 byte zero-padded big endian unsigned
138+
// integers
139+
#[cfg(all(test, feature = "crypto_nss"))]
140+
pub fn read_p256_sig(z: &[u8]) -> Result<Vec<u8>> {
141+
// Strip the tag and length.
142+
let (z, rest) = expect_tag_with_short_len(TAG_SEQUENCE, z)?;
143+
144+
// The input should not have any trailing data.
145+
if !rest.is_empty() {
146+
return Err(CryptoError::MalformedInput);
147+
}
148+
149+
let read_u256 = |z| -> Result<(&[u8], &[u8])> {
150+
let (r, z) = expect_tag_with_short_len(TAG_INTEGER, z)?;
151+
// We're expecting r < 2^256, so no more than 33 bytes as a signed integer.
152+
if r.is_empty() || r.len() > 33 {
153+
return Err(CryptoError::MalformedInput);
154+
}
155+
// If it is 33 bytes the leading byte must be zero.
156+
if r.len() == 33 && r[0] != 0 {
157+
return Err(CryptoError::MalformedInput);
158+
}
159+
// Ensure r is no more than 32 bytes.
160+
if r.len() == 33 {
161+
Ok((&r[1..], z))
162+
} else {
163+
Ok((r, z))
164+
}
165+
};
166+
167+
let (r, z) = read_u256(z)?;
168+
let (s, z) = read_u256(z)?;
169+
170+
// We should have consumed the entire buffer
171+
if !z.is_empty() {
172+
return Err(CryptoError::MalformedInput);
173+
}
174+
175+
// Left pad each integer with zeros to length 32 and concatenate the results
176+
let mut out = vec![0u8; 64];
177+
{
178+
let (r_out, s_out) = out.split_at_mut(32);
179+
r_out[32 - r.len()..].copy_from_slice(r);
180+
s_out[32 - s.len()..].copy_from_slice(s);
181+
}
182+
Ok(out)
183+
}

0 commit comments

Comments
 (0)