Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b1b3196
fix: remove PEN supported
bkioshn Sep 11, 2024
8a045e4
fix(rust/c509-certificate): time
bkioshn Sep 11, 2024
4b73607
fix(rust/c509-certificate): handle if issuer not set, set to subject
bkioshn Sep 11, 2024
3e70fc0
fix(rust/c509-certificate): remove rdn
bkioshn Sep 11, 2024
afe628d
fix(rust/c509-certificate): change certificate version number
bkioshn Sep 11, 2024
0e30d15
fix(rust/c509-certificate): rearrange tbscertificate
bkioshn Sep 11, 2024
069640b
fix(rust/c509-certificate): update docs
bkioshn Sep 12, 2024
b2d22c8
fix(rust/c509-certificate): format + err handling
bkioshn Sep 12, 2024
7cb3f21
Merge branch 'main' into feat/c509-v11
bkioshn Sep 12, 2024
b94854d
Merge branch 'main' into feat/c509-v11
stevenj Sep 17, 2024
c50834a
fix(rust/c509-certificate): Rename functions + add necessary function…
bkioshn Sep 17, 2024
ddf93d2
fix(rust/c509-certificate): CBOR decode encode helper (#27)
bkioshn Sep 19, 2024
61984b8
Merge branch 'main' into feat/c509-v11
stevenj Sep 21, 2024
a88a032
Merge branch 'main' into feat/c509-v11
stevenj Sep 21, 2024
b288110
test(rust/c509-certificate): Fix test for draft11 (#28)
bkioshn Sep 21, 2024
e49224d
test(rust/c509-certificate): break the cache
stevenj Sep 21, 2024
c04d0b1
fix(rust): Lint and lintfix locally in release build mode
stevenj Sep 21, 2024
01009e7
test(rust/c509-certificate): rename tbs_cert.rs to cert_tbs.rs to tes…
stevenj Sep 21, 2024
84983e4
docs(rust/cardano-chain-follower): Fix list of enhancements we could …
stevenj Sep 23, 2024
9b9e37f
ci(rust): Don't break the cache now in a build, but do check the cach…
stevenj Sep 23, 2024
391cb80
test(rust): In an abundance of caution make sure cached src isn't use…
stevenj Sep 24, 2024
17e28dc
Merge branch 'main' into feat/c509-v11
stevenj Sep 24, 2024
91fc0eb
Merge branch 'main' into feat/c509-v11
bkioshn Sep 30, 2024
edc69a2
Merge branch 'main' into feat/c509-v11
stevenj Sep 30, 2024
3b7bf5b
fix(rust) earthfile
bkioshn Sep 30, 2024
389485a
Merge branch 'main' into feat/c509-v11
bkioshn Oct 2, 2024
84fe964
fix(rust/c509-certificate): update docs (#39)
bkioshn Oct 3, 2024
b92837b
Merge branch 'main' into feat/c509-v11
stevenj Oct 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; This c509 Certificate format is based upon:
; https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/
; https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/11/
; And is restricted/customized to better enable compatibility with Plutus scripts
; that would consume them, without loosing necessary features of x509
; Not all x509 features are supported and some fields have different semantics to improve
Expand All @@ -11,42 +11,32 @@ C509CertificatePlutusRestrictedSubset = [ TBSCertificate, issuerSignatureValue:

; The elements of the following group are used in a CBOR Sequence:
TBSCertificate = (
c509CertificateType: &c509CertificateTypeValues, ; Always 0
c509CertificateType: int, ; Always 2 as natively signed C509 certificate following X.509 v3
certificateSerialNumber: CertificateSerialNumber, ; Can be ignored/set to 0 or used as intended.
issuer: Name, ; This could be an on-chain reference to the issuer cert, what would be the best way? Transaction hash/cert hash?
validityNotBefore: Time, ; c509 uses UTC
validityNotAfter: Time, ; c509 uses UTC
issuerSignatureAlgorithm: AlgorithmIdentifier, ; Must be int(12) = Ed25519
issuer: Name / null, ; If set to null, use the `subject`. This could be an on-chain reference to
the issuer cert, what would be the best way? Transaction hash/cert hash?
validityNotBefore: ~time, ; UTC
validityNotAfter: ~time / null, ; UTC
subject: Name, ; Reference to on-chain keys related to this certificate
subjectPublicKeyAlgorithm: AlgorithmIdentifier, ; Must be int(12) = Ed25519
subjectPublicKey: subjectPublicKey, ; Ed25519 public key
extensions: Extensions, ; No extensions are currently supported must be set to []
issuerSignatureAlgorithm: AlgorithmIdentifier, ; Must be int(12) = Ed25519
)

; 0 = Native CBOR Certificate type
; 1 = reencoded-der-cert - Not supported in this restricted version of the format.
c509CertificateTypeValues = ( native-cbor: 0,
; reencoded-der: 1 ; Not supported in this restricted encoding format
subjectPublicKey: any, ; Ed25519 public key
extensions: Extensions, ; Currently support extensions with basic CBOR types and Alternative Name
)

CertificateSerialNumber = biguint

Name = [ * RelativeDistinguishedName ]
/ text
/ bytes
CertificateSerialNumber = ~biguint

RelativeDistinguishedName = Attribute / [ 2* Attribute ]
Name = [ * Attribute ] / text / bytes

Attribute = (
( attributeType: int, attributeValue: text )
// ( attributeType: oid, attributeValue: bytes )
// ( attributeType: pen, attributeValue: bytes )
// CardanoPublicKey
)

subjectPublicKey = bytes .size (32..32); Ed25519 public key stored in bytes, adjust size of this if other key types are supported.

; This is a completely custom Attribute for the RelativeDistinguishedName which is only for use with Plutus scripts.
; This is a completely custom Attribute, which is only for use with Plutus scripts.
; attributeType = The type of Cardano key we associate with this certificate.
; proof = Does the transaction require proof that the key is owned by the transaction signer?
; attributeValue = The Cardano public key hash of the attribute type
Expand All @@ -61,14 +51,12 @@ cardanoKeyTypes = (
ccHotVerificationKeyHash: 4,
)

; Plutus will need to convert the Unix epoch timestamp to the nearest slot number
; For `~time` Plutus will need to convert the Unix epoch timestamp to the nearest slot number
; validityNotBefore rounds up to the next Slot after that time.
; validityNotAfter rounds down to the next Slot before that time.
Time = ( ~time / null )

ed25519Signature = bstr .size 64; Ed25519 signature must be tagged to identify their type.


; Currently ONLY AlgorithmIdentifier int(12) - Ed25519 is supported.
; oid and [ algorithm: oid, parameters: bytes ] are not supported by Plutus.
AlgorithmIdentifier = (int
Expand All @@ -82,5 +70,4 @@ Extensions = [ * Extension ] / int
Extension = (
( extensionID: int, extensionValue: any )
// ( extensionID: ~oid, ? critical: true, extensionValue: bytes )
// ( extensionID: pen, ? critical: true, extensionValue: bytes )
)
6 changes: 3 additions & 3 deletions rust/c509-certificate/examples/cli/data/cert_sample_1.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"self_signed": true,
"c509_certificate_type": 0,
"c509_certificate_type": 2,
"certificate_serial_number": 128269,
"issuer_signature_algorithm": null,
"issuer": [
{
"oid": "2.5.4.3",
Expand All @@ -24,6 +25,5 @@
"value": { "int": 1 },
"critical": false
}
],
"issuer_signature_algorithm": null
]
}
85 changes: 50 additions & 35 deletions rust/c509-certificate/examples/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use std::{

use asn1_rs::{oid, Oid};
use c509_certificate::{
attributes::Attributes,
big_uint::UnwrappedBigUint,
extensions::Extensions,
issuer_sig_algo::IssuerSignatureAlgorithm,
name::{rdn::RelativeDistinguishedName, Name, NameValue},
name::{Name, NameValue},
signing::{PrivateKey, PublicKey},
subject_pub_key_algo::SubjectPubKeyAlgorithm,
tbs_cert::TbsCert,
Expand Down Expand Up @@ -102,17 +103,20 @@ struct C509Json {
/// Optional serial number of the certificate,
/// if not provided, a random number will be generated.
serial_number: Option<UnwrappedBigUint>,
/// Optional issuer signature algorithm of the certificate,
/// if not provided, set to Ed25519.
issuer_signature_algorithm: Option<IssuerSignatureAlgorithm>,
/// Optional issuer of the certificate,
/// if not provided, issuer is the same as subject.
issuer: Option<RelativeDistinguishedName>,
issuer: Option<Attributes>,
/// Optional validity not before date,
/// if not provided, set to current time.
validity_not_before: Option<String>,
/// Optional validity not after date,
/// if not provided, set to no expire date 9999-12-31T23:59:59+00:00.
validity_not_after: Option<String>,
/// Relative distinguished name of the subject.
subject: RelativeDistinguishedName,
/// Attributes of the subject.
subject: Attributes,
/// Optional subject public key algorithm of the certificate,
/// if not provided, set to Ed25519.
subject_public_key_algorithm: Option<SubjectPubKeyAlgorithm>,
Expand All @@ -121,9 +125,6 @@ struct C509Json {
subject_public_key: String,
/// Extensions of the certificate.
extensions: Extensions,
/// Optional issuer signature algorithm of the certificate,
/// if not provided, set to Ed25519.
issuer_signature_algorithm: Option<IssuerSignatureAlgorithm>,
/// Optional issuer signature value of the certificate.
#[serde(skip_deserializing)]
issuer_signature_value: Option<Vec<u8>>,
Expand All @@ -133,9 +134,9 @@ struct C509Json {
const ED25519: (Oid, Option<String>) = (oid!(1.3.101 .112), None);

/// Integer indicate that certificate is self-signed.
/// 0 for Natively Signed C509 Certificate following X.509 v3
/// 1 for CBOR re-encoding of X.509 v3 Certificate
const SELF_SIGNED_INT: u8 = 0;
/// 2 for Natively Signed C509 Certificate following X.509 v3
/// 3 for CBOR re-encoding of X.509 v3 Certificate
const SELF_SIGNED_INT: u8 = 2;

// -------------------generate-----------------------

Expand All @@ -159,7 +160,12 @@ fn generate(

// Parse validity dates or use defaults
// Now for not_before date
let not_before = parse_or_default_date(c509_json.validity_not_before, Utc::now().timestamp())?;
let now_timestamp: u64 = Utc::now()
.timestamp()
.try_into()
.map_err(|_| anyhow::anyhow!("Current timestamp is invalid"))?;

let not_before = parse_or_default_date(c509_json.validity_not_before, now_timestamp)?;
// Default as expire date for not_after
// Expire date = 9999-12-31T23:59:59+00:00 as mention in the C509 document
let not_after = parse_or_default_date(
Expand All @@ -175,18 +181,18 @@ fn generate(
let tbs = TbsCert::new(
c509_json.certificate_type.unwrap_or(SELF_SIGNED_INT),
serial_number,
Name::new(NameValue::RelativeDistinguishedName(issuer)),
c509_json
.issuer_signature_algorithm
.unwrap_or(IssuerSignatureAlgorithm::new(key_type.0.clone(), ED25519.1)),
Some(Name::new(NameValue::Attributes(issuer))),
Time::new(not_before),
Time::new(not_after),
Name::new(NameValue::RelativeDistinguishedName(c509_json.subject)),
Name::new(NameValue::Attributes(c509_json.subject)),
c509_json
.subject_public_key_algorithm
.unwrap_or(SubjectPubKeyAlgorithm::new(key_type.0.clone(), key_type.1)),
.unwrap_or(SubjectPubKeyAlgorithm::new(key_type.0, key_type.1)),
public_key.to_bytes(),
c509_json.extensions.clone(),
c509_json
.issuer_signature_algorithm
.unwrap_or(IssuerSignatureAlgorithm::new(key_type.0, ED25519.1)),
);

let cert = c509_certificate::generate(&tbs, private_key)?;
Expand All @@ -213,9 +219,8 @@ fn write_to_output_file(output: PathBuf, data: &[u8]) -> anyhow::Result<()> {
/// If self-signed is true, issuer is the same as subject.
/// Otherwise, issuer must be present.
fn determine_issuer(
self_signed: bool, issuer: Option<RelativeDistinguishedName>,
subject: RelativeDistinguishedName,
) -> anyhow::Result<RelativeDistinguishedName> {
self_signed: bool, issuer: Option<Attributes>, subject: Attributes,
) -> anyhow::Result<Attributes> {
if self_signed {
Ok(subject)
} else {
Expand Down Expand Up @@ -249,12 +254,16 @@ fn get_key_type(key_type: &Option<String>) -> anyhow::Result<(Oid<'static>, Opti
}
}

/// Parse date string to i64.
fn parse_or_default_date(date_option: Option<String>, default: i64) -> Result<i64, anyhow::Error> {
/// Parse date string to u64.
fn parse_or_default_date(date_option: Option<String>, default: u64) -> Result<u64, anyhow::Error> {
match date_option {
Some(date) => {
DateTime::parse_from_rfc3339(&date)
.map(|dt| dt.timestamp())
.map(|dt| {
dt.timestamp()
.try_into()
.map_err(|_| anyhow::anyhow!("Timestamp is invalid"))
})?
.map_err(|e| anyhow::anyhow!(format!("Failed to parse date {date}: {e}",)))
},
None => Ok(default),
Expand Down Expand Up @@ -294,15 +303,15 @@ fn decode(file: &PathBuf, output: Option<PathBuf>) -> anyhow::Result<()> {
self_signed: is_self_signed,
certificate_type: Some(tbs_cert.get_c509_certificate_type()),
serial_number: Some(tbs_cert.get_certificate_serial_number().clone()),
issuer: Some(extract_relative_distinguished_name(tbs_cert.get_issuer())?),
validity_not_before: Some(time_to_string(tbs_cert.get_validity_not_before().to_i64())?),
validity_not_after: Some(time_to_string(tbs_cert.get_validity_not_after().to_i64())?),
subject: extract_relative_distinguished_name(tbs_cert.get_subject())?,
issuer_signature_algorithm: Some(tbs_cert.get_issuer_signature_algorithm().clone()),
issuer: Some(extract_attributes(tbs_cert.get_issuer())?),
validity_not_before: Some(time_to_string(tbs_cert.get_validity_not_before().to_u64())?),
validity_not_after: Some(time_to_string(tbs_cert.get_validity_not_after().to_u64())?),
subject: extract_attributes(tbs_cert.get_subject())?,
subject_public_key_algorithm: Some(tbs_cert.get_subject_public_key_algorithm().clone()),
// Return a hex formation of the public key
subject_public_key: tbs_cert.get_subject_public_key().encode_hex(),
extensions: tbs_cert.get_extensions().clone(),
issuer_signature_algorithm: Some(tbs_cert.get_issuer_signature_algorithm().clone()),
issuer_signature_value: c509.get_issuer_signature_value().clone(),
};

Expand All @@ -316,18 +325,24 @@ fn decode(file: &PathBuf, output: Option<PathBuf>) -> anyhow::Result<()> {
Ok(())
}

/// Extract a `RelativeDistinguishedName` from a `Name`.
fn extract_relative_distinguished_name(name: &Name) -> anyhow::Result<RelativeDistinguishedName> {
/// Extract a `Attributes` from a `Name`.
fn extract_attributes(name: &Name) -> anyhow::Result<Attributes> {
match name.get_value() {
NameValue::RelativeDistinguishedName(rdn) => Ok(rdn.clone()),
_ => Err(anyhow::anyhow!("Expected RelativeDistinguishedName")),
NameValue::Attributes(attrs) => Ok(attrs.clone()),
_ => Err(anyhow::anyhow!("Expected Attributes")),
}
}

/// Convert time in i64 to string.
fn time_to_string(time: i64) -> anyhow::Result<String> {
let datetime =
DateTime::from_timestamp(time, 0).ok_or_else(|| anyhow::anyhow!("Invalid timestamp"))?;
fn time_to_string(time: u64) -> anyhow::Result<String> {
// Attempt to convert the timestamp and handle errors if they occur
let timestamp: i64 = time
.try_into()
.map_err(|e| anyhow::anyhow!("Failed to convert time: {:?}", e))?;

// Convert the timestamp to a DateTime and handle any potential errors
let datetime = DateTime::from_timestamp(timestamp, 0)
.ok_or_else(|| anyhow::anyhow!("Invalid timestamp"))?;
Ok(datetime.to_rfc3339())
}

Expand Down
8 changes: 4 additions & 4 deletions rust/c509-certificate/examples/web/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Testing the wasm binding JS functions.

import init, {
generate,
verify,
decode,
PublicKey,
generate,
PrivateKey,
PublicKey,
verify,
} from "../../pkg/c509_certificate.js";

const pem_sk = `
Expand All @@ -24,7 +24,7 @@ const tbs = {
c509_certificate_type: 0,
certificate_serial_number: 1000000n,
issuer: {
relative_distinguished_name: [
attributes: [
{
oid: "2.5.4.3",
value: [{ text: "RFC test CA" }],
Expand Down
2 changes: 1 addition & 1 deletion rust/c509-certificate/src/algorithm_identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! not implemented yet.
//!
//! For more information about `AlgorithmIdentifier`,
//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/)
//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/11/)

use asn1_rs::Oid;
use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder};
Expand Down
14 changes: 2 additions & 12 deletions rust/c509-certificate/src/attributes/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
//!
//! ```cddl
//! Attribute = ( attributeType: int, attributeValue: text ) //
//! ( attributeType: ~oid, attributeValue: bytes ) //
//! ( attributeType: pen, attributeValue: bytes )
//! ( attributeType: ~oid, attributeValue: bytes ) //
//! ```
//!
//! For more information about Attribute,
//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/)
//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/11/)

use std::str::FromStr;

Expand Down Expand Up @@ -55,15 +54,6 @@ impl Attribute {
&self.value
}

/// Set whether `Attribute` can be PEN encoded.
pub(crate) fn set_pen_supported(self) -> Self {
Self {
registered_oid: self.registered_oid.pen_encoded(),
multi_value: self.multi_value,
value: self.value,
}
}

/// Set whether `Attribute` can have multiple value.
pub(crate) fn set_multi_value(mut self) -> Self {
self.multi_value = true;
Expand Down
2 changes: 1 addition & 1 deletion rust/c509-certificate/src/attributes/data.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Attribute data provides a necessary information for encoding and decoding of C509
//! Attribute. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/)
//! Attribute. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/11/)
//! Section 9.3 C509 Attributes Registry for more information.

use anyhow::Error;
Expand Down
Loading