summaryrefslogtreecommitdiffstats
path: root/sq/src/commands/merge_signatures.rs
blob: d7d38bb7a990062459de8bc5eb3a9404d8e6c48e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use anyhow::Context as _;
use std::io;

extern crate sequoia_openpgp as openpgp;
use crate::create_or_stdout;
use crate::openpgp::packet::Literal;
use crate::openpgp::packet::Tag;
use crate::openpgp::parse::{PacketParser, PacketParserResult, Parse};
use crate::openpgp::serialize::stream::{LiteralWriter, Message};
use crate::openpgp::serialize::Serialize;
use crate::openpgp::{Packet, Result};

pub fn merge_signatures(
    input1: &mut (dyn io::Read + Send + Sync),
    input2: &mut (dyn io::Read + Send + Sync),
    output_path: Option<&str>,
) -> Result<()> {
    let parser1 =
        PacketParser::from_reader(input1).context("Failed to build parser")?;
    let parser2 =
        PacketParser::from_reader(input2).context("Failed to build parser")?;
    let mut output = create_or_stdout(output_path, false)?;

    let mut sink = Message::new(&mut output);

    let (ops1, post_ops_parser1) = read_while_by_tag(parser1, Tag::OnePassSig)?;
    let (ops2, post_ops_parser2) = read_while_by_tag(parser2, Tag::OnePassSig)?;

    let ops1 = ops1
        .into_iter()
        .map(ops_with_last_false)
        .collect::<Result<Vec<_>>>()?;

    write_packets(ops1, &mut sink)?;
    write_packets(ops2, &mut sink)?;

    let (sink_new, post_literal_parser1, post_literal_parser2) =
        compare_and_write_literal(sink, post_ops_parser1, post_ops_parser2)?;
    sink = sink_new;

    let (sigs2, _) = read_while_by_tag(post_literal_parser2, Tag::Signature)?;
    let (sigs1, _) = read_while_by_tag(post_literal_parser1, Tag::Signature)?;
    write_packets(sigs2, &mut sink)?;
    write_packets(sigs1, &mut sink)?;

    sink.finalize().context("Failed to write data")?;
    Ok(())
}

fn ops_with_last_false(p: Packet) -> Result<Packet> {
    if let Packet::OnePassSig(mut ops) = p {
        ops.set_last(false);
        Ok(Packet::OnePassSig(ops))
    } else {
        Err(anyhow::anyhow!("Not a OnePassSig packet"))
    }
}

fn write_packets(packets: Vec<Packet>, mut sink: &mut Message) -> Result<()> {
    for packet in packets {
        packet.serialize(&mut sink)?;
    }
    Ok(())
}

fn compare_and_write_literal<'a, 'b, 'c>(
    sink: Message<'a>,
    ppr1: PacketParserResult<'b>,
    ppr2: PacketParserResult<'c>,
) -> Result<(Message<'a>, PacketParserResult<'b>, PacketParserResult<'c>)> {
    // We want to compare the bodies of the literal packets, by comparing their digests.
    // Digests are only known after reading the packets, so:
    // First, move both parsers past the literal packet, copy out the body of one of them.
    // Second, compare the packets which now include the correct hashes,
    // normalize to ignore metadata.
    let (mut lp1, ppr1) = read_while_by_tag(ppr1, Tag::Literal)?;
    let lp1 = lp1.remove(0);

    let (sink, lp2, ppr2) = write_literal_(sink, ppr2)?;

    let lp1 = normalize_literal(lp1)?;
    let lp2 = normalize_literal(lp2)?;
    eprintln!("lp1: {:?}", lp1);
    eprintln!("lp2: {:?}", lp2);

    if lp1 == lp2 {
        Ok((sink, ppr1, ppr2))
    } else {
        Err(anyhow::anyhow!("Literal Packets differ, aborting!"))
    }
}

// Clear date and filename.
fn normalize_literal(p: Packet) -> Result<Literal> {
    if let Packet::Literal(mut l) = p {
        l.set_date(None)?;
        l.set_filename(&[])?;
        Ok(l)
    } else {
        Err(anyhow::anyhow!("Not a literal packet"))
    }
}

fn write_literal_<'a, 'b>(
    mut sink: Message<'a>,
    ppr: PacketParserResult<'b>,
) -> Result<(Message<'a>, Packet, PacketParserResult<'b>)> {
    if let PacketParserResult::Some(mut pp) = ppr {
        // Assemble a new Literal packet.
        // Cannot use packet.serialize because that does not include the body.
        if let Packet::Literal(l) = pp.packet.clone() {
            // Create a literal writer to wrap the data in a literal
            // message packet.
            let mut literal = LiteralWriter::new(sink)
                .format(l.format())
                .build()
                .context("Failed to create literal writer")?;
            // Do not add any metadata as it is unprotected anyway.

            // Just copy all the data.
            io::copy(&mut pp, &mut literal).context("Failed to copy data")?;

            // Pop the literal writer.
            sink = literal
                .finalize_one()
                .context("Failed to write literal packet")?
                .unwrap();
        }

        let foo = pp.recurse()?; //TODO rename
        Ok((sink, foo.0, foo.1))
    } else {
        Err(anyhow::anyhow!("Unexpected end of file"))
    }
}

fn read_while_by_tag(
    mut ppr: PacketParserResult,
    tag: Tag,
) -> Result<(Vec<Packet>, PacketParserResult)> {
    let mut result = vec![];

    while let PacketParserResult::Some(pp) = ppr {
        let next_tag_matches = pp.header().ctb().tag() == tag;
        if !next_tag_matches {
            return Ok((result, PacketParserResult::Some(pp)));
        }

        // Start parsing the next packet, recursing.
        let (packet, next_ppr) = pp.recurse()?;
        ppr = next_ppr;
        result.push(packet);
    }

    Ok((result, ppr))
}