diff --git a/src/lib.rs b/src/lib.rs index b19bbe0..4aca7fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,11 +63,8 @@ pub enum Error { /// The SCT contained an invalid signature. InvalidSignature, - /// The SCT referenced a Log that has an invalid public key encoding. - InvalidKey, - /// The SCT was signed in the future. Clock skew? - SCTTimestampInFuture, + TimestampInFuture, /// The SCT had a version that this library does not handle. UnsupportedSCTVersion, @@ -141,6 +138,11 @@ fn write_u24(v: u32, out: &mut Vec) { out.push(v as u8); } +fn write_u16(v: u16, out: &mut Vec) { + out.push((v >> 8) as u8); + out.push(v as u8); +} + #[derive(Debug)] struct SCT<'a> { log_id: &'a [u8], @@ -157,7 +159,6 @@ const RSA_PKCS1_SHA384: u16 = 0x0501; const SCT_V1: u8 = 0u8; const SCT_TIMESTAMP: u8 = 0u8; const SCT_X509_ENTRY: [u8; 2] = [0, 0]; -const SCT_NO_EXTENSION: [u8; 2] = [0, 0]; impl<'a> SCT<'a> { fn verify(&self, key: &[u8], cert: &[u8]) -> Result<(), Error> { @@ -176,7 +177,8 @@ impl<'a> SCT<'a> { data.extend_from_slice(&SCT_X509_ENTRY); write_u24(cert.len() as u32, &mut data); data.extend_from_slice(cert); - data.extend_from_slice(&SCT_NO_EXTENSION); + write_u16(self.exts.len() as u16, &mut data); + data.extend_from_slice(self.exts); let sig = untrusted::Input::from(self.sig); let data = untrusted::Input::from(&data); @@ -249,7 +251,7 @@ pub fn verify_sct(cert: &[u8], sct.verify(log.key, cert)?; if sct.timestamp > at_time { - return Err(Error::SCTTimestampInFuture); + return Err(Error::TimestampInFuture); } Ok(i) diff --git a/src/testdata/ecdsa_p256-basic-sct.bin b/src/testdata/ecdsa_p256-basic-sct.bin index e75fe84..74420b9 100644 Binary files a/src/testdata/ecdsa_p256-basic-sct.bin and b/src/testdata/ecdsa_p256-basic-sct.bin differ diff --git a/src/testdata/ecdsa_p256-future-sct.bin b/src/testdata/ecdsa_p256-future-sct.bin new file mode 100644 index 0000000..e0d1107 Binary files /dev/null and b/src/testdata/ecdsa_p256-future-sct.bin differ diff --git a/src/testdata/ecdsa_p256-junk-sct.bin b/src/testdata/ecdsa_p256-junk-sct.bin new file mode 100644 index 0000000..523c6fd Binary files /dev/null and b/src/testdata/ecdsa_p256-junk-sct.bin differ diff --git a/src/testdata/ecdsa_p256-short-sct.bin b/src/testdata/ecdsa_p256-short-sct.bin new file mode 100644 index 0000000..e0d1107 Binary files /dev/null and b/src/testdata/ecdsa_p256-short-sct.bin differ diff --git a/src/testdata/ecdsa_p256-version-sct.bin b/src/testdata/ecdsa_p256-version-sct.bin new file mode 100644 index 0000000..283d1be Binary files /dev/null and b/src/testdata/ecdsa_p256-version-sct.bin differ diff --git a/src/testdata/ecdsa_p256-wrongcert-sct.bin b/src/testdata/ecdsa_p256-wrongcert-sct.bin new file mode 100644 index 0000000..6f31667 Binary files /dev/null and b/src/testdata/ecdsa_p256-wrongcert-sct.bin differ diff --git a/src/testdata/ecdsa_p256-wrongext-sct.bin b/src/testdata/ecdsa_p256-wrongext-sct.bin new file mode 100644 index 0000000..ef78481 Binary files /dev/null and b/src/testdata/ecdsa_p256-wrongext-sct.bin differ diff --git a/src/testdata/ecdsa_p256-wrongid-sct.bin b/src/testdata/ecdsa_p256-wrongid-sct.bin new file mode 100644 index 0000000..6b7081c Binary files /dev/null and b/src/testdata/ecdsa_p256-wrongid-sct.bin differ diff --git a/src/testdata/ecdsa_p256-wrongtime-sct.bin b/src/testdata/ecdsa_p256-wrongtime-sct.bin new file mode 100644 index 0000000..ad9893c Binary files /dev/null and b/src/testdata/ecdsa_p256-wrongtime-sct.bin differ diff --git a/src/testdata/ecdsa_p384-basic-sct.bin b/src/testdata/ecdsa_p384-basic-sct.bin index 02e06e1..b9e06a0 100644 Binary files a/src/testdata/ecdsa_p384-basic-sct.bin and b/src/testdata/ecdsa_p384-basic-sct.bin differ diff --git a/src/testdata/ecdsa_p384-wrongcert-sct.bin b/src/testdata/ecdsa_p384-wrongcert-sct.bin new file mode 100644 index 0000000..d8c61b2 Binary files /dev/null and b/src/testdata/ecdsa_p384-wrongcert-sct.bin differ diff --git a/src/testdata/ecdsa_p384-wrongtime-sct.bin b/src/testdata/ecdsa_p384-wrongtime-sct.bin new file mode 100644 index 0000000..ab54f11 Binary files /dev/null and b/src/testdata/ecdsa_p384-wrongtime-sct.bin differ diff --git a/src/testdata/rsa2048-wrongcert-sct.bin b/src/testdata/rsa2048-wrongcert-sct.bin new file mode 100644 index 0000000..034f7a2 Binary files /dev/null and b/src/testdata/rsa2048-wrongcert-sct.bin differ diff --git a/src/testdata/rsa2048-wrongtime-sct.bin b/src/testdata/rsa2048-wrongtime-sct.bin new file mode 100644 index 0000000..8315467 Binary files /dev/null and b/src/testdata/rsa2048-wrongtime-sct.bin differ diff --git a/src/testdata/rsa3072-wrongcert-sct.bin b/src/testdata/rsa3072-wrongcert-sct.bin new file mode 100644 index 0000000..52b68af Binary files /dev/null and b/src/testdata/rsa3072-wrongcert-sct.bin differ diff --git a/src/testdata/rsa3072-wrongtime-sct.bin b/src/testdata/rsa3072-wrongtime-sct.bin new file mode 100644 index 0000000..0621c4e Binary files /dev/null and b/src/testdata/rsa3072-wrongtime-sct.bin differ diff --git a/src/testdata/rsa4096-wrongcert-sct.bin b/src/testdata/rsa4096-wrongcert-sct.bin new file mode 100644 index 0000000..2f35fbc Binary files /dev/null and b/src/testdata/rsa4096-wrongcert-sct.bin differ diff --git a/src/testdata/rsa4096-wrongtime-sct.bin b/src/testdata/rsa4096-wrongtime-sct.bin new file mode 100644 index 0000000..7e05dd6 Binary files /dev/null and b/src/testdata/rsa4096-wrongtime-sct.bin differ diff --git a/src/tests_generated.rs b/src/tests_generated.rs index 8a64042..dfaba9d 100644 --- a/src/tests_generated.rs +++ b/src/tests_generated.rs @@ -1,4 +1,22 @@ -use super::{Log, verify_sct}; +use super::{Log, Error, verify_sct}; + +static TEST_LOG_ECDSA_P256: Log = Log { + description: "fake test ecdsa_p256 log", + url: "", + operated_by: "random python script", + max_merge_delay: 0, + key: include_bytes!("testdata/ecdsa-prime256v1-pub.raw"), + id: [0x71, 0xdc, 0x5e, 0xdb, 0xf0, 0x13, 0xd3, 0x88, 0x8a, 0x14, 0x6f, 0x49, 0x3d, 0xbe, 0x33, 0x94, 0xbb, 0x5a, 0xdb, 0x65, 0xb2, 0x6a, 0x96, 0xe2, 0x38, 0x35, 0x4e, 0xd4, 0x8f, 0xeb, 0xb2, 0x4f], +}; + +static TEST_LOG_ECDSA_P384: Log = Log { + description: "fake test ecdsa_p384 log", + url: "", + operated_by: "random python script", + max_merge_delay: 0, + key: include_bytes!("testdata/ecdsa-secp384r1-pub.raw"), + id: [0x29, 0xbb, 0xef, 0x00, 0xba, 0xd9, 0x3d, 0x5d, 0x4c, 0x03, 0xc7, 0x29, 0xe9, 0x4d, 0xb6, 0xac, 0x00, 0xe0, 0xfd, 0x28, 0xf6, 0x46, 0x56, 0x37, 0x24, 0xac, 0x58, 0xdc, 0x66, 0xb1, 0x99, 0xe9], +}; static TEST_LOG_RSA2048: Log = Log { description: "fake test rsa2048 log", @@ -27,76 +45,236 @@ static TEST_LOG_RSA4096: Log = Log { id: [0xfb, 0x56, 0x27, 0x12, 0xec, 0xa0, 0xf0, 0xdc, 0x7f, 0x06, 0xda, 0x76, 0xab, 0xba, 0x5d, 0x88, 0x28, 0x2b, 0x62, 0xc5, 0x71, 0xf6, 0x0d, 0x69, 0x41, 0x94, 0x85, 0x16, 0xc8, 0x22, 0xf3, 0x29], }; -static TEST_LOG_ECDSA_P256: Log = Log { - description: "fake test ecdsa_p256 log", - url: "", - operated_by: "random python script", - max_merge_delay: 0, - key: include_bytes!("testdata/ecdsa-prime256v1-pub.raw"), - id: [0x71, 0xdc, 0x5e, 0xdb, 0xf0, 0x13, 0xd3, 0x88, 0x8a, 0x14, 0x6f, 0x49, 0x3d, 0xbe, 0x33, 0x94, 0xbb, 0x5a, 0xdb, 0x65, 0xb2, 0x6a, 0x96, 0xe2, 0x38, 0x35, 0x4e, 0xd4, 0x8f, 0xeb, 0xb2, 0x4f], -}; +#[test] +pub fn ecdsa_p256_basic() { + let sct = include_bytes!("testdata/ecdsa_p256-basic-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P256]; + let now = 1235; -static TEST_LOG_ECDSA_P384: Log = Log { - description: "fake test ecdsa_p384 log", - url: "", - operated_by: "random python script", - max_merge_delay: 0, - key: include_bytes!("testdata/ecdsa-secp384r1-pub.raw"), - id: [0x29, 0xbb, 0xef, 0x00, 0xba, 0xd9, 0x3d, 0x5d, 0x4c, 0x03, 0xc7, 0x29, 0xe9, 0x4d, 0xb6, 0xac, 0x00, 0xe0, 0xfd, 0x28, 0xf6, 0x46, 0x56, 0x37, 0x24, 0xac, 0x58, 0xdc, 0x66, 0xb1, 0x99, 0xe9], -}; + assert_eq!(Ok(0), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p256_wrongtime() { + let sct = include_bytes!("testdata/ecdsa_p256-wrongtime-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P256]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p256_wrongcert() { + let sct = include_bytes!("testdata/ecdsa_p256-wrongcert-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P256]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p384_basic() { + let sct = include_bytes!("testdata/ecdsa_p384-basic-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P384]; + let now = 1235; + + assert_eq!(Ok(0), + verify_sct(cert, sct, now, &logs)); +} #[test] -pub fn test_basic_rsa2048() { +pub fn ecdsa_p384_wrongtime() { + let sct = include_bytes!("testdata/ecdsa_p384-wrongtime-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P384]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p384_wrongcert() { + let sct = include_bytes!("testdata/ecdsa_p384-wrongcert-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P384]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn rsa2048_basic() { let sct = include_bytes!("testdata/rsa2048-basic-sct.bin"); let cert = b"cert"; let logs = [&TEST_LOG_RSA2048]; let now = 1235; - - assert_eq!(0, - verify_sct(cert, sct, now, &logs).unwrap()); + + assert_eq!(Ok(0), + verify_sct(cert, sct, now, &logs)); } #[test] -pub fn test_basic_rsa3072() { +pub fn rsa2048_wrongtime() { + let sct = include_bytes!("testdata/rsa2048-wrongtime-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_RSA2048]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn rsa2048_wrongcert() { + let sct = include_bytes!("testdata/rsa2048-wrongcert-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_RSA2048]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn rsa3072_basic() { let sct = include_bytes!("testdata/rsa3072-basic-sct.bin"); let cert = b"cert"; let logs = [&TEST_LOG_RSA3072]; let now = 1235; - - assert_eq!(0, - verify_sct(cert, sct, now, &logs).unwrap()); + + assert_eq!(Ok(0), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn rsa3072_wrongtime() { + let sct = include_bytes!("testdata/rsa3072-wrongtime-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_RSA3072]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn rsa3072_wrongcert() { + let sct = include_bytes!("testdata/rsa3072-wrongcert-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_RSA3072]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); } #[test] -pub fn test_basic_rsa4096() { +pub fn rsa4096_basic() { let sct = include_bytes!("testdata/rsa4096-basic-sct.bin"); let cert = b"cert"; let logs = [&TEST_LOG_RSA4096]; let now = 1235; - - assert_eq!(0, - verify_sct(cert, sct, now, &logs).unwrap()); + + assert_eq!(Ok(0), + verify_sct(cert, sct, now, &logs)); } #[test] -pub fn test_basic_ecdsa_p256() { - let sct = include_bytes!("testdata/ecdsa_p256-basic-sct.bin"); +pub fn rsa4096_wrongtime() { + let sct = include_bytes!("testdata/rsa4096-wrongtime-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_RSA4096]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn rsa4096_wrongcert() { + let sct = include_bytes!("testdata/rsa4096-wrongcert-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_RSA4096]; + let now = 1235; + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p256_junk() { + let sct = include_bytes!("testdata/ecdsa_p256-junk-sct.bin"); let cert = b"cert"; let logs = [&TEST_LOG_ECDSA_P256]; let now = 1235; - - assert_eq!(0, - verify_sct(cert, sct, now, &logs).unwrap()); + + assert_eq!(Err(Error::MalformedSCT), + verify_sct(cert, sct, now, &logs)); } #[test] -pub fn test_basic_ecdsa_p384() { - let sct = include_bytes!("testdata/ecdsa_p384-basic-sct.bin"); +pub fn ecdsa_p256_wrongid() { + let sct = include_bytes!("testdata/ecdsa_p256-wrongid-sct.bin"); let cert = b"cert"; - let logs = [&TEST_LOG_ECDSA_P384]; + let logs = [&TEST_LOG_ECDSA_P256]; + let now = 1235; + + assert_eq!(Err(Error::UnknownLog), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p256_version() { + let sct = include_bytes!("testdata/ecdsa_p256-version-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P256]; + let now = 1235; + + assert_eq!(Err(Error::UnsupportedSCTVersion), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p256_future() { + let sct = include_bytes!("testdata/ecdsa_p256-future-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P256]; + let now = 1233; + + assert_eq!(Err(Error::TimestampInFuture), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p256_wrongext() { + let sct = include_bytes!("testdata/ecdsa_p256-wrongext-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P256]; let now = 1235; - - assert_eq!(0, - verify_sct(cert, sct, now, &logs).unwrap()); + + assert_eq!(Err(Error::InvalidSignature), + verify_sct(cert, sct, now, &logs)); +} + +#[test] +pub fn ecdsa_p256_short() { + let sct = include_bytes!("testdata/ecdsa_p256-short-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_ECDSA_P256]; + let now = 1234; + + for l in 0..118 { + assert_eq!(Err(Error::MalformedSCT), + verify_sct(cert, &sct[..l], now, &logs)); + } } diff --git a/test/mktest.py b/test/mktest.py index b9e9b56..a996384 100644 --- a/test/mktest.py +++ b/test/mktest.py @@ -26,6 +26,7 @@ def __init__(self): def sign(self, key, alg, cert): to_sign = struct.pack('!BBQHBH', self.version, self.type, self.timestamp, self.enttype, 0, len(cert)) \ + cert + self.exts + open('sigin.bin', 'w').write(to_sign) sig = subprocess.check_output(['openssl', 'dgst', '-' + SIGALG_HASH[alg], '-sign', key, 'sigin.bin']) self.sig = struct.pack('!HH', alg, len(sig)) + sig @@ -33,6 +34,16 @@ def sign(self, key, alg, cert): def encode(self): return struct.pack('!B32sQ', self.version, self.id, self.timestamp) + self.exts + self.sig + def copy(self): + c = SCT() + c.__dict__ = self.__dict__.copy() + return c + + def having(self, **kwargs): + copy = self.copy() + copy.__dict__.update(**kwargs) + return copy + def genrsa(len): priv, pub = 'rsa-%d-priv.pem' % len, 'rsa-%d-pub.pem' % len if not path.exists(pub): @@ -98,11 +109,11 @@ def format_bytes(b): return ', '.join(map(lambda x: '0x%02x' % ord(x), b)) keys = [ + ('ecdsa_p256', genecdsa('prime256v1')), + ('ecdsa_p384', genecdsa('secp384r1')), ('rsa2048', genrsa(2048)), ('rsa3072', genrsa(3072)), ('rsa4096', genrsa(4096)), - ('ecdsa_p256', genecdsa('prime256v1')), - ('ecdsa_p384', genecdsa('secp384r1')), ] algs = dict( @@ -113,13 +124,13 @@ def format_bytes(b): ecdsa_p384 = SIGALG_ECDSA_SHA384 ) -print 'use super::{Log, verify_sct};' +print 'use super::{Log, Error, verify_sct};' print for name, (priv, pub) in keys: pubder = convert_der(pub) pubraw = pubder.replace('.der', '.raw') - open(pubraw, 'w').write(raw_public_key(open(pubder).read())) + open('../src/testdata/' + pubraw, 'w').write(raw_public_key(open(pubder).read())) print """static TEST_LOG_%s: Log = Log { description: "fake test %s log", @@ -134,21 +145,84 @@ def format_bytes(b): pubraw, format_bytes(keyhash(pub))) -for name, (priv, pub) in keys: - sct = SCT() - sct.sign(priv, algs[name], 'cert') - sct.id = keyhash(pub) +def emit_test(keyname, sctname, encoding, timestamp = 1235, expect = 'Ok(0)', extra = ''): + open('../src/testdata/%s-%s-sct.bin' % (keyname, sctname), 'w').write(encoding) + + print """#[test] +pub fn %(keyname)s_%(sctname)s() { + let sct = include_bytes!("testdata/%(keyname)s-%(sctname)s-sct.bin"); + let cert = b"cert"; + let logs = [&TEST_LOG_%(keyname_up)s]; + let now = %(time)d; + + assert_eq!(%(expect)s, + verify_sct(cert, sct, now, &logs)); +} +""" % dict(time = timestamp, + sctname = sctname, + keyname = keyname, + keyname_up = keyname.upper(), + expect = expect) - open('%s-basic-sct.bin' % name, 'w').write(sct.encode()) +def emit_short_test(keyname, sctname, encoding, expect): + open('../src/testdata/%s-%s-sct.bin' % (keyname, sctname), 'w').write(encoding) print """#[test] -pub fn test_basic_%s() { - let sct = include_bytes!("testdata/%s-basic-sct.bin"); +pub fn %(keyname)s_%(sctname)s() { + let sct = include_bytes!("testdata/%(keyname)s-%(sctname)s-sct.bin"); let cert = b"cert"; - let logs = [&TEST_LOG_%s]; - let now = 1235; - - assert_eq!(0, - verify_sct(cert, sct, now, &logs).unwrap()); + let logs = [&TEST_LOG_%(keyname_up)s]; + let now = 1234; + + for l in 0..%(len)d { + assert_eq!(%(expect)s, + verify_sct(cert, &sct[..l], now, &logs)); + } } -""" % (name, name, name.upper()) +""" % dict(sctname = sctname, + keyname = keyname, + keyname_up = keyname.upper(), + expect = expect, + len = len(encoding)) + +# basic tests of each key type +for name, (priv, pub) in keys: + sct = SCT() + sct.sign(priv, algs[name], 'cert') + sct.id = keyhash(pub) + + emit_test(name, 'basic', sct.encode()) + + emit_test(name, 'wrongtime', + sct.having(timestamp = 123).encode(), + expect = 'Err(Error::InvalidSignature)') + + sct.sign(priv, algs[name], 'adsqweqweqwekimqwelqwmel') + emit_test(name, 'wrongcert', sct.encode(), expect = 'Err(Error::InvalidSignature)') + +# other tests, only for a particular key type +name, (priv, pub) = keys[0] + +sct = SCT() +sct.sign(priv, algs[name], 'cert') +sct.id = keyhash(pub) + +emit_test(name, 'junk', + sct.encode() + 'a', + expect = 'Err(Error::MalformedSCT)') +emit_test(name, 'wrongid', + sct.having(id = '\x00' * 32).encode(), + expect = 'Err(Error::UnknownLog)') +emit_test(name, 'version', + sct.having(version = 1).encode(), + expect = 'Err(Error::UnsupportedSCTVersion)') +emit_test(name, 'future', + sct.encode(), + timestamp = 1233, + expect = 'Err(Error::TimestampInFuture)') +emit_test(name, 'wrongext', + sct.having(exts = '\x00\x01A').encode(), + expect = 'Err(Error::InvalidSignature)') +emit_short_test(name, 'short', + sct.encode(), + expect = 'Err(Error::MalformedSCT)')