[dependencies]p256 = { version = "=0.14.0", features = ["ecdsa"] }hex = "=0.4.3" use p256::ecdsa::Signature; /// DER (SEQUENCE of two INTEGERs) to fixed-width r‖s: 64 bytes on P-256.fn der_to_raw(der: &[u8]) -> Result<[u8; 64], p256::ecdsa::Error> { Ok(Signature::from_der(der)?.to_bytes().into())} /// Fixed-width r‖s to minimal ASN.1 DER (70–72 bytes on P-256).fn raw_to_der(raw: &[u8]) -> Result<Vec<u8>, p256::ecdsa::Error> { Ok(Signature::from_slice(raw)?.to_der().as_bytes().to_vec())} fn main() -> Result<(), Box<dyn std::error::Error>> { let der = hex::decode("30450221009f4118b059a34a2f344c39a1d085d93bab560402938281dc1cb2f3dfb8eee58602200bd54b34a72042551c3316e10f461a1d69036ba4f0d72d95d42465340b56def8")?; let raw = der_to_raw(&der)?; println!("raw: {}", hex::encode(raw)); println!("round trip: {}", raw_to_der(&raw)? == der); Ok(())} How it works
Signature::from_derparses strict DER, strips the sign bytes and left-pads r and s to 32 bytes;to_bytes()returns the fixed 64-byter‖sarray.Signature::from_slicetakes exactly 64 bytes;to_der()emits minimal DER INTEGERs with a0x00sign byte only where the top bit is set.- Both constructors reject values that are not valid scalars (zero, or not below the group order), so a converted signature is at least structurally a P-256 signature.
- The tests in the run convert two real signatures whose r or s is only 31 bytes long, and check that each format is refused where the other is expected.
Gotchas
- The signature type is per curve:
p256::ecdsa::Signatureonly handles 32-byte scalars. Usep384::ecdsa::Signature(96-byte raw) orp521::ecdsa::Signature(132-byte raw, long-form DER length) for the other curves — a P-384 DER fails to parse here instead of being truncated. - Conversion keeps
sas it is. If the consumer demands low-S, callnormalize_s()beforeto_der()orto_bytes(). - Converting does not prove the signature verifies — only that it is well-formed. A failing verification after a correct conversion points at the key, the hash or the message bytes.
- Often no conversion is needed at all:
VerifyingKey::verifyaccepts aSignaturebuilt from either encoding, so parse with the matching constructor and verify directly.