cardano_message_signing/
builders.rs

1use super::*;
2
3
4#[derive(Clone, Debug)]
5pub struct COSESign1Builder {
6    headers: Headers,
7    payload: Vec<u8>,
8    external_aad: Option<Vec<u8>>,
9    is_payload_external: bool,
10    hashed: bool,
11}
12
13
14impl COSESign1Builder {
15    pub fn new(headers: &Headers, payload: Vec<u8>, is_payload_external: bool) -> Self {
16        let mut all_headers = headers.clone();
17        all_headers.unprotected.set_header(
18            &Label::new_text(String::from("hashed")),
19            &CBORValue::new_special(&CBORSpecial::new_bool(false))).unwrap();
20        Self {
21            headers: all_headers,
22            payload,
23            external_aad: None,
24            is_payload_external,
25            hashed: false,
26        }
27    }
28
29    pub fn hash_payload(&mut self) {
30        if self.hashed {
31            self.hashed = true;
32            // self.headers.unprotected.set_header(
33            //     &Label::new_text(String::from("hashed")),
34            //     &Value::Special(CBORSpecial::Bool(true)));
35            self.payload = crypto::blake2b224(self.payload.as_ref()).to_vec();
36        }
37    }
38
39    pub fn set_external_aad(&mut self, external_aad: Vec<u8>) {
40        self.external_aad = Some(external_aad);
41    }
42
43    pub fn make_data_to_sign(&self) -> SigStructure {
44        SigStructure::new(
45            SigContext::Signature1,
46            &self.headers.protected,
47            self.external_aad.clone().unwrap_or(vec![]),
48            self.payload.clone())
49    }
50
51    pub fn build(&self, signed_sig_structure: Vec<u8>) -> COSESign1 {
52        COSESign1::new(
53            &self.headers,
54            match self.is_payload_external {
55                true => None,
56                false => Some(self.payload.clone()),
57            },
58            signed_sig_structure
59        )
60    }
61}
62
63
64
65#[derive(Clone, Debug)]
66pub struct COSESignBuilder {
67    headers: Headers,
68    payload: Vec<u8>,
69    external_aad: Option<Vec<u8>>,
70    is_payload_external: bool,
71    hashed: bool,
72}
73
74
75impl COSESignBuilder {
76    pub fn new(headers: &Headers, payload: Vec<u8>, is_payload_external: bool) -> Self {
77        Self {
78            headers: headers.clone(),
79            payload,
80            external_aad: None,
81            is_payload_external,
82            hashed: false,
83        }
84    }
85
86    pub fn hash_payload(&mut self) {
87        if self.hashed {
88            self.hashed = true;
89            self.payload = crypto::blake2b224(self.payload.as_ref()).to_vec();
90        }
91    }
92
93    pub fn set_external_aad(&mut self, external_aad: Vec<u8>) {
94        self.external_aad = Some(external_aad);
95    }
96
97    pub fn make_data_to_sign(&self) -> SigStructure {
98        SigStructure::new(
99            SigContext::Signature,
100            &self.headers.protected,
101            self.external_aad.clone().unwrap_or(vec![]),
102            self.payload.clone())
103    }
104
105    pub fn build(&self, signed_sig_structure: &COSESignatures) -> COSESign {
106        COSESign::new(
107            &self.headers,
108            match self.is_payload_external {
109                true => None,
110                false => Some(self.payload.clone())
111            },
112            signed_sig_structure)
113    }
114}
115
116// TODO: copy the COSESign(1) builders for COSEEncrypt(1) if this seems like a good approach
117
118label_enum!(AlgorithmId {
119    /// EdDSA (Pure EdDSA, not HashedEdDSA) - the algorithm used for Cardano addresses
120    EdDSA = -8,
121    /// ChaCha20/Poly1305 w/ 256-bit key, 128-bit tag
122    ChaCha20Poly1305 = 24,
123});
124
125label_enum!(KeyType {
126    /// octet key pair
127    OKP = 1,
128    /// 2-coord EC
129    EC2 = 2,
130    Symmetric = 4,
131});
132
133label_enum!(ECKey {
134    // EC identifier
135    CRV = -1,
136    // x coord (OKP) / pubkey (EC2)
137    X = -2,
138    // y coord (only for EC2 - not present in OKP)
139    Y = -3,
140    // private key (optional)
141    D = -4,
142});
143
144label_enum!(CurveType {
145    P256 = 1,
146    P384 = 2,
147    P521 = 3,
148    X25519 = 4,
149    X448 = 5,
150    // the EdDSA variant used for cardano addresses
151    Ed25519 = 6,
152    Ed448 = 7,
153});
154
155label_enum!(KeyOperation {
156    // The key is used to create signatures. Requires private key fields
157    Sign = 1,
158    // The key is used for verification of signatures.
159    Verify = 2,
160    // The key is used for key transport encryption. 
161    Encrypt = 3,
162    // The key is used for key transport decryption. Requires private key fields.
163    Decrypt = 4,
164    // The key is used for key wrap encryption.
165    WrapKey = 5,
166    // The key is used for key wrap decryption. Requires private key fields.
167    UnwrapKey = 6,
168    // The key is used for deriving keys. Requires private key fields
169    DeriveKey = 7,
170    // The key is used for deriving bits not to be used as a key. Requires private key fields
171    DeriveBits = 8,
172});
173
174
175#[derive(Clone, Debug)]
176pub struct EdDSA25519Key {
177    pubkey_bytes: Vec<u8>,
178    prvkey_bytes: Option<Vec<u8>>,
179    for_signing: bool,
180    for_verifying: bool,
181}
182
183
184impl EdDSA25519Key {
185    pub fn new(pubkey_bytes: Vec<u8>) -> Self {
186        Self {
187            pubkey_bytes,
188            prvkey_bytes: None,
189            for_signing: false,
190            for_verifying: false,
191        }
192    }
193
194    pub fn set_private_key(&mut self, private_key_bytes: Vec<u8>) {
195        self.prvkey_bytes = Some(private_key_bytes);
196    }
197
198    pub fn is_for_signing(&mut self) {
199        self.for_signing = true;
200    }
201
202    pub fn is_for_verifying(&mut self) {
203        self.for_verifying = true;
204    }
205
206    pub fn build(&self) -> COSEKey {
207        let mut key = COSEKey::new(&KeyType::OKP.into());
208        // crv
209        key.other_headers.insert(
210            ECKey::CRV.into(),
211            CBORValue::from_label(&Label::from(CurveType::Ed25519)));
212        // x
213        key.other_headers.insert(
214            ECKey::X.into(),
215            CBORValue::new_bytes(self.pubkey_bytes.clone()));
216        // d (privkey)
217        if let Some(d) = &self.prvkey_bytes {
218            key.other_headers.insert(
219                ECKey::D.into(),
220                CBORValue::new_bytes(d.clone()));
221        }
222        // alg
223        key.set_algorithm_id(&AlgorithmId::EdDSA.into());
224        // key-ops
225        if self.for_signing || self.for_verifying {
226            let mut key_ops = Labels::new();
227            if self.for_signing {
228                key_ops.add(&KeyOperation::Sign.into());
229            }
230            if self.for_verifying {
231                key_ops.add(&KeyOperation::Verify.into());
232            }
233            key.set_key_ops(&key_ops);
234        }
235        key
236    }
237}
238
239// TODO: a way to parse/check from COSEKey -> EdDSA25519 variant. Or should this be a wrapper not a builder?
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    // #[test]
246    // fn cose_sign1() {
247    //     let mut protected_header = HeaderMap::new();
248    //     protected_header.set_algorithm_id(&Label::new_int(&Int::new_i32(x: i32)));
249    //     let headers = Headers::new(&protected, &unprotected);
250    //     let payload = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
251    //     let mut builder = COSESign1Builder::new(&headers, payload, true);
252    // }
253
254    #[test]
255    fn eddsa25519key() {
256        let xpub = vec![0; 32];
257        let key = EdDSA25519Key::new(xpub.clone()).build();
258        // these serve to test the enum implementations too
259        assert_eq!(key.key_type().as_int().unwrap().as_i32().unwrap(), 1);
260        assert_eq!(key.algorithm_id().unwrap().as_int().unwrap().as_i32().unwrap(), -8);
261        assert_eq!(key.header(&Label::new_int(&Int::new_i32(-1))).unwrap().as_int().unwrap().as_i32().unwrap(), 6);
262        assert_eq!(key.header(&Label::new_int(&Int::new_i32(-2))).unwrap().as_bytes().unwrap(), xpub);
263    }
264}