use crate::keystore::traits::{public_key_from_sec1, signature_from_raw};
use crate::{DevicePublicKey, DeviceSignature, Error, Result};
const COORDINATE_LEN: usize = 32;
pub(crate) fn fixed_width(bytes: &[u8]) -> Result<[u8; COORDINATE_LEN]> {
let significant = match bytes.iter().position(|byte| *byte != 0) {
Some(position) => &bytes[position..],
None => &[],
};
if significant.len() > COORDINATE_LEN {
return Err(Error::InvalidPublicKey {
reason: "a coordinate of the public key is wider than the curve",
});
}
let mut out = [0_u8; COORDINATE_LEN];
out[COORDINATE_LEN - significant.len()..].copy_from_slice(significant);
Ok(out)
}
pub(crate) fn sec1_from_coordinates(x: &[u8], y: &[u8]) -> Result<DevicePublicKey> {
let mut encoded = [0_u8; 65];
encoded[0] = 0x04;
encoded[1..33].copy_from_slice(&fixed_width(x)?);
encoded[33..].copy_from_slice(&fixed_width(y)?);
public_key_from_sec1(&encoded)
}
pub(crate) fn signature_from_der(der: &[u8]) -> Result<DeviceSignature> {
let signature = p256::ecdsa::Signature::from_der(der).map_err(|_| Error::Hardware {
operation: "Signature.sign",
code: 0,
})?;
signature_from_raw(&signature.to_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keystore::traits::DIGEST_LEN;
use p256::ecdsa::{Signature, SigningKey, signature::hazmat::PrehashSigner};
#[test]
fn a_signed_coordinate_loses_its_sign_byte() {
let mut padded = vec![0x00];
padded.extend([0xAB; 32]);
assert_eq!(fixed_width(&padded).expect("32 bytes"), [0xAB; 32]);
}
#[test]
fn a_short_coordinate_is_padded_on_the_left() {
let short = [0x01, 0x02, 0x03];
let mut expected = [0_u8; 32];
expected[29..].copy_from_slice(&short);
assert_eq!(fixed_width(&short).expect("32 bytes"), expected);
}
#[test]
fn a_zero_coordinate_is_thirty_two_zero_bytes() {
assert_eq!(fixed_width(&[0x00]).expect("32 bytes"), [0x00; 32]);
assert_eq!(fixed_width(&[]).expect("32 bytes"), [0x00; 32]);
}
#[test]
fn an_overlong_coordinate_is_rejected() {
assert!(fixed_width(&[0x01; 33]).is_err());
}
#[test]
fn coordinates_assemble_into_a_point_the_curve_accepts() {
let signing = SigningKey::from_slice(&[7_u8; 32]).expect("a valid scalar");
let point = signing.verifying_key().to_sec1_point(false);
let (x, y) = (point.x().expect("x"), point.y().expect("y"));
let assembled = sec1_from_coordinates(x, y).expect("a point");
assert_eq!(assembled.as_sec1_bytes().len(), 65);
assert_eq!(assembled.as_sec1_bytes()[0], 0x04);
assert_eq!(assembled.as_sec1_bytes(), point.as_bytes());
}
#[test]
fn coordinates_off_the_curve_are_rejected() {
assert!(sec1_from_coordinates(&[0xFF; 32], &[0xFF; 32]).is_err());
}
#[test]
fn a_der_signature_becomes_the_raw_one() {
let signing = SigningKey::from_slice(&[11_u8; 32]).expect("a valid scalar");
let digest = [3_u8; DIGEST_LEN];
let signature: Signature = signing.sign_prehash(&digest).expect("a signature");
let raw = signature_from_der(signature.to_der().as_bytes()).expect("parsable");
assert_eq!(raw.as_bytes(), signature.to_bytes().as_slice());
}
#[test]
fn a_broken_der_signature_is_rejected() {
assert!(signature_from_der(&[0x30, 0x02, 0x01, 0x00]).is_err());
assert!(signature_from_der(&[]).is_err());
assert!(signature_from_der(&[0xFF; 8]).is_err());
}
}