Skip to content

Commit 9aa0456

Browse files
committed
Fulcio API v2.
Updates sigstore.rs to use the latest Fulcio API v2 to request certs. This required a change in the structures used to send the request because the payload changed in the new API version. Signed-off-by: José Guilherme Vanz <[email protected]>
1 parent d0744c8 commit 9aa0456

File tree

2 files changed

+102
-39
lines changed

2 files changed

+102
-39
lines changed

src/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,7 @@ pub enum SigstoreError {
210210

211211
#[error(transparent)]
212212
PKCS1Error(#[from] pkcs1::Error),
213+
214+
#[error("Failed to request the certificate")]
215+
CertificateRequestError,
213216
}

src/fulcio/mod.rs

Lines changed: 99 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use base64::{engine::general_purpose::STANDARD as BASE64_STD_ENGINE, Engine as _
88
use openidconnect::core::CoreIdToken;
99
use reqwest::Body;
1010
use serde::ser::SerializeStruct;
11-
use serde::{Serialize, Serializer};
11+
use serde::{Deserialize, Serialize, Serializer};
1212
use std::convert::{TryFrom, TryInto};
1313
use std::fmt::{Debug, Display, Formatter};
1414
use url::Url;
@@ -17,19 +17,63 @@ use url::Url;
1717
pub const FULCIO_ROOT: &str = "https://fulcio.sigstore.dev/";
1818

1919
/// Path within Fulcio to obtain a signing certificate.
20-
pub const SIGNING_CERT_PATH: &str = "api/v1/signingCert";
20+
pub const SIGNING_CERT_PATH: &str = "api/v2/signingCert";
2121

2222
const CONTENT_TYPE_HEADER_NAME: &str = "content-type";
2323

24+
#[derive(Serialize, Debug)]
25+
#[serde(rename_all = "camelCase")]
26+
struct Credentials {
27+
oidc_identity_token: String,
28+
}
29+
30+
#[derive(Debug)]
31+
struct PublicKey {
32+
algorithm: Option<SigningScheme>,
33+
content: String,
34+
}
35+
impl Serialize for PublicKey {
36+
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
37+
where
38+
S: Serializer,
39+
{
40+
let mut pk = serializer.serialize_struct("PublicKey", 2)?;
41+
pk.serialize_field("content", &self.content)?;
42+
pk.serialize_field(
43+
"algorithm",
44+
match self.algorithm {
45+
Some(SigningScheme::ECDSA_P256_SHA256_ASN1)
46+
| Some(SigningScheme::ECDSA_P384_SHA384_ASN1) => "ECDSA",
47+
Some(SigningScheme::ED25519) => "ED25519",
48+
Some(SigningScheme::RSA_PSS_SHA256(_))
49+
| Some(SigningScheme::RSA_PSS_SHA384(_))
50+
| Some(SigningScheme::RSA_PSS_SHA512(_))
51+
| Some(SigningScheme::RSA_PKCS1_SHA256(_))
52+
| Some(SigningScheme::RSA_PKCS1_SHA384(_))
53+
| Some(SigningScheme::RSA_PKCS1_SHA512(_)) => "RSA",
54+
_ => "PUBLIC_KEY_ALGORITHM_UNSPECIFIED",
55+
},
56+
)?;
57+
pk.end()
58+
}
59+
}
60+
61+
#[derive(Serialize, Debug)]
62+
#[serde(rename_all = "camelCase")]
63+
struct PublicKeyRequest {
64+
public_key: PublicKey,
65+
proof_of_possession: String,
66+
}
2467
/// Fulcio certificate signing request
2568
///
2669
/// Used to present a public key and signed challenge/proof-of-key in exchange
2770
/// for a signed X509 certificate in return.
2871
#[derive(Serialize, Debug)]
2972
#[serde(rename_all = "camelCase")]
3073
struct Csr {
31-
public_key: Option<PublicKey>,
32-
signed_email_address: Option<String>,
74+
credentials: Credentials,
75+
public_key_request: PublicKeyRequest,
76+
certificate_signing_request: Option<String>,
3377
}
3478

3579
impl TryFrom<Csr> for Body {
@@ -40,37 +84,33 @@ impl TryFrom<Csr> for Body {
4084
}
4185
}
4286

43-
/// Internal newtype to control serde jsonification.
44-
#[derive(Debug)]
45-
struct PublicKey(String, SigningScheme);
87+
#[derive(Deserialize)]
88+
#[serde(rename_all = "camelCase")]
89+
struct Chain {
90+
certificates: Vec<FulcioCert>,
91+
}
4692

47-
impl Serialize for PublicKey {
48-
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
49-
where
50-
S: Serializer,
51-
{
52-
let mut pk = serializer.serialize_struct("PublicKey", 2)?;
53-
pk.serialize_field("content", &self.0)?;
54-
pk.serialize_field(
55-
"algorithm",
56-
match self.1 {
57-
SigningScheme::ECDSA_P256_SHA256_ASN1 | SigningScheme::ECDSA_P384_SHA384_ASN1 => {
58-
"ecdsa"
59-
}
60-
SigningScheme::ED25519 => "ed25519",
61-
SigningScheme::RSA_PSS_SHA256(_)
62-
| SigningScheme::RSA_PSS_SHA384(_)
63-
| SigningScheme::RSA_PSS_SHA512(_)
64-
| SigningScheme::RSA_PKCS1_SHA256(_)
65-
| SigningScheme::RSA_PKCS1_SHA384(_)
66-
| SigningScheme::RSA_PKCS1_SHA512(_) => "rsa",
67-
},
68-
)?;
69-
pk.end()
70-
}
93+
#[derive(Deserialize)]
94+
#[serde(rename_all = "camelCase")]
95+
struct SignedCertificateDetachedSct {
96+
chain: Chain,
97+
}
98+
99+
#[derive(Deserialize)]
100+
#[serde(rename_all = "camelCase")]
101+
struct SignedCertificateEmbeddedSct {
102+
chain: Chain,
103+
}
104+
105+
#[derive(Deserialize)]
106+
#[serde(rename_all = "camelCase")]
107+
struct CsrResponse {
108+
signed_certificate_detached_sct: Option<SignedCertificateDetachedSct>,
109+
signed_certificate_embedded_sct: Option<SignedCertificateEmbeddedSct>,
71110
}
72111

73112
/// The PEM-encoded certificate chain returned by Fulcio.
113+
#[derive(Deserialize, Clone)]
74114
pub struct FulcioCert(String);
75115

76116
impl AsRef<[u8]> for FulcioCert {
@@ -139,12 +179,19 @@ impl FulcioClient {
139179
let signature = BASE64_STD_ENGINE.encode(signature);
140180

141181
let key_pair = signer.to_sigstore_keypair()?;
142-
let public_key = key_pair.public_key_to_der()?;
143-
let public_key = BASE64_STD_ENGINE.encode(public_key);
144-
182+
let public_key = key_pair.public_key_to_pem()?;
145183
let csr = Csr {
146-
public_key: Some(PublicKey(public_key, signing_scheme)),
147-
signed_email_address: Some(signature),
184+
credentials: Credentials {
185+
oidc_identity_token: token.to_string(),
186+
},
187+
public_key_request: PublicKeyRequest {
188+
public_key: PublicKey {
189+
algorithm: Some(signing_scheme),
190+
content: public_key,
191+
},
192+
proof_of_possession: signature,
193+
},
194+
certificate_signing_request: None,
148195
};
149196

150197
let csr: Body = csr.try_into()?;
@@ -159,11 +206,24 @@ impl FulcioClient {
159206
.await
160207
.map_err(|_| SigstoreError::SigstoreFulcioCertificatesNotProvidedError)?;
161208

162-
let cert = response
163-
.text()
209+
let cert_response = response
210+
.json::<CsrResponse>()
164211
.await
165212
.map_err(|_| SigstoreError::SigstoreFulcioCertificatesNotProvidedError)?;
166213

167-
Ok((signer, FulcioCert(cert)))
214+
let cert: FulcioCert;
215+
216+
if let Some(signed_certificate_detached_sct) = cert_response.signed_certificate_detached_sct
217+
{
218+
cert = signed_certificate_detached_sct.chain.certificates[0].clone();
219+
} else if let Some(signed_certificate_embedded_sct) =
220+
cert_response.signed_certificate_embedded_sct
221+
{
222+
cert = signed_certificate_embedded_sct.chain.certificates[0].clone();
223+
} else {
224+
return Err(SigstoreError::CertificateRequestError);
225+
}
226+
227+
Ok((signer, cert))
168228
}
169229
}

0 commit comments

Comments
 (0)