On this page· 3

Open and verify a pack

  • file format 6
  • Rust SDK 1.0 preview

Goal: open a pack, read its verify report, then change one letter and see what is reported.

Steps

  1. 1. Open the bytes with Pack::from_bytes and OpenOptions::default(). The default verifies steps 1 to 8.
  2. 2. Call pack.verify() for a report. On a good pack, report.is_ok() is true.
  3. 3. Change one byte of the record text: Person becomes person. Opening the changed bytes with the default options fails with checksum_mismatch.
  4. 4. Open the changed bytes with OpenOptions::no_verify() and call verify(). The report has sha256_ok: false.

Needs the Rust SDK

verify.rsrust
// Verify a pack, and see what a damaged pack reports.
use plxi_sdk::{write, ErrorKind, OpenOptions, Pack, Record};

fn main() -> Result<(), plxi_sdk::Error> {
    let records = vec![
        Record::from_json_line(r#"{"k":"ent","id":"e1","t":"Person"}"#)?,
        Record::from_json_line(r#"{"k":"ent","id":"e2","t":"Person"}"#)?,
    ];
    let good = write(&records, None)?;

    // A good pack: every member of the report is fine.
    let pack = Pack::from_bytes(good.clone(), OpenOptions::default())?;
    let report = pack.verify()?;
    assert!(report.is_ok());
    let count = &report.record_count;
    assert_eq!((count.declared, count.actual), (2, 2));
    println!("{}", report.to_json());

    // Change one letter in the record text after the pack was sealed:
    // "Person" becomes "person". The JSON is still valid; the digest
    // no longer matches.
    let mut damaged = good;
    let at = damaged
        .windows(6)
        .position(|w| w == b"Person")
        .expect("the record text contains \"Person\"");
    damaged[at] = b'p';

    // The default open refuses it and names the reason.
    let err = Pack::from_bytes(damaged.clone(), OpenOptions::default())
        .unwrap_err();
    assert_eq!(err.kind(), ErrorKind::ChecksumMismatch);
    assert_eq!(err.kind().as_str(), "checksum_mismatch");
    println!("refused: {err}");

    // With verification off the pack opens, and the report says what
    // is wrong with it.
    let pack = Pack::from_bytes(damaged, OpenOptions::no_verify())?;
    let report = pack.verify()?;
    assert!(!report.sha256_ok);
    assert!(!report.is_ok());
    assert_eq!(report.record_count.actual, 2); // the records still parse
    println!("{}", report.to_json());
    Ok(())
}
Output
{"sha256_ok":true,"header_footer_agree":true,"record_count":{"declared":2,"actual":2},"appendix":{"present":false,"file_checksum_ok":true,"sections":[]},"embrefs":{"total":0,"resolved":0,"dangling":0,"type_mismatch":0}}
refused: checksum_mismatch: the file's SHA-256 digest does not match the one recorded in its footer
{"sha256_ok":false,"header_footer_agree":true,"record_count":{"declared":2,"actual":2},"appendix":{"present":false,"file_checksum_ok":true,"sections":[]},"embrefs":{"total":0,"resolved":0,"dangling":0,"type_mismatch":0}}

Ran with cargo run --example 02_verify · exit status 0

The first line is the report for the good pack. The second is the refusal: the error kind, a colon, then a message for people. The third is the report for the changed pack, opened without verification.

Table 1. Members of the verify report: 5
MemberMeaning
sha256_okthe computed digest equals the footer's
header_footer_agreea final header line equals the footer; a placeholder header counts as agreeing
record_countdeclared is the footer's count; actual is the number of record lines read
appendixpresent; file_checksum_ok; and sections, one {index, type, crc_ok} per section
embrefscounts of bindings: total, resolved, dangling, type_mismatch

Things to know

  • Opening verifies by default. no_verify() exists for the case shown: reading a report from a pack already known to be damaged. Records read from a pack opened that way may come from a damaged body.
  • The message after the kind is for people and can change between releases. Code branches on the kind.
  • If the changed byte breaks a record's JSON, verify() returns an error, json or invalid_record, instead of a report. This guide changes a letter inside a string so the line still parses.
  • Pack::open does the same for a file path, and adds io when the file cannot be read.

Reference

Sections