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
/*
* Rekor
*
* Rekor is a cryptographically secure, immutable transparency log for signed software releases.
*
* The version of the OpenAPI document: 0.0.1
*
* Generated by: https://openapi-generator.tech
*/
use crate::crypto::merkle::{MerkleProofVerifier, Rfc6269Default, hex_to_hash_output};
use crate::errors::SigstoreError;
use crate::errors::SigstoreError::ConsistencyProofError;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use sha2::digest::Output;
/// Used to deserialize responses of the Rekor API.
#[derive(Clone, Serialize, Deserialize)]
pub struct RekorConsistencyProof {
/// The hash value in hex form stored at the root of the merkle tree at the time the proof was
/// generated
#[serde(rename = "rootHash")]
pub root_hash: String,
// the hashes stored in hex form
#[serde(rename = "hashes")]
pub hashes: Vec<String>,
}
/// Represents a Merkle consistency proof for a transparency log.
///
/// This struct is typically constructed from the log's API response in [`RekorConsistencyProof`].
///
/// It contains the hashes (as bytestring arrays) required to prove that a newer Merkle tree is an
/// append-only extension of a previous tree, as well as the root hash of the tree at the time the
/// proof was generated. This is used to verify the append-only property of the log between two
/// tree sizes.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct ConsistencyProof {
/// The hash value, in bytestring, stored at the root of the merkle tree at the time the proof
/// was generated
pub root_hash: [u8; 32],
// the hashes stored in bytestring form
pub hashes: Vec<[u8; 32]>,
}
impl TryFrom<RekorConsistencyProof> for ConsistencyProof {
type Error = crate::errors::SigstoreError;
fn try_from(raw: RekorConsistencyProof) -> Result<Self, Self::Error> {
// let root_hash = <[u8; 32]>::from_hex(&raw.root_hash)?;
let root_hash = hex_to_hash_output(&raw.root_hash)?.into();
let hashes = raw
.hashes
.into_iter()
.map(hex_to_hash_output)
.map(|r| r.map(Into::into))
.collect::<Result<_, _>>()?;
Ok(ConsistencyProof { root_hash, hashes })
}
}
impl ConsistencyProof {
pub fn new(root_hash: [u8; 32], hashes: Vec<[u8; 32]>) -> ConsistencyProof {
ConsistencyProof { root_hash, hashes }
}
/// Verify this consistency proof against the given parameters.
/// If `new_root` is `Some` then this root will be used in the verification. If it is `None`
/// then the root in `self.root_hash` is used.
pub fn verify(
&self,
old_size: u64,
old_root: &[u8; 32],
new_size: u64,
new_root: Option<&[u8; 32]>,
) -> Result<(), SigstoreError> {
// convert hashes from bytestring and into Sha256
let proof_hashes: Vec<Output<Sha256>> = self
.hashes
.iter()
.map(|h| Output::<Sha256>::from(*h))
.collect();
let new_root = match new_root {
Some(s) => s,
None => &self.root_hash,
};
Rfc6269Default::verify_consistency(
old_size,
new_size,
&proof_hashes,
old_root.into(),
new_root.into(),
)
.map_err(ConsistencyProofError)
}
}