identity_storage/key_storage/
keytool.rs1use async_trait::async_trait;
5use identity_verification::jwk::FromJwk as _;
6use identity_verification::jwk::Jwk;
7use identity_verification::jwk::ToJwk as _;
8use identity_verification::jws::JwsAlgorithm;
9use iota_interaction::types::base_types::IotaAddress;
10use iota_interaction::types::crypto::IotaKeyPair;
11use iota_interaction::types::crypto::SignatureScheme;
12use iota_interaction::KeytoolStorage;
13
14use super::JwkGenOutput;
15use super::JwkStorage;
16use super::KeyId;
17use super::KeyStorageError;
18use super::KeyStorageErrorKind;
19use super::KeyStorageResult;
20use super::KeyType;
21
22#[cfg_attr(feature = "send-sync-storage", async_trait)]
23#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
24impl JwkStorage for KeytoolStorage {
25 async fn generate(&self, key_type: KeyType, alg: JwsAlgorithm) -> KeyStorageResult<JwkGenOutput> {
26 let key_scheme = match key_type.as_str() {
27 "ed25519" => SignatureScheme::ED25519,
28 "secp256r1" => SignatureScheme::Secp256r1,
29 "secp256k1" => SignatureScheme::Secp256k1,
30 _ => return Err(KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)),
31 };
32
33 check_key_alg_compatibility(&key_type, &alg)?;
34
35 let (pk, _alias) = self
36 .generate_key(key_scheme)
37 .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_source(e))?;
38
39 let address = IotaAddress::from(&pk);
40 let mut jwk = pk
41 .to_jwk()
42 .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_source(e))?;
43 jwk.set_kid(jwk.thumbprint_sha256_b64());
44
45 Ok(JwkGenOutput {
46 key_id: KeyId::new(address.to_string()),
47 jwk,
48 })
49 }
50
51 async fn insert(&self, jwk: Jwk) -> KeyStorageResult<KeyId> {
52 let sk = IotaKeyPair::from_jwk(&jwk)
53 .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::RetryableIOFailure).with_source(e))?;
54 let pk = sk.public();
55 let _alias = self
56 .insert_key(sk)
57 .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::RetryableIOFailure).with_source(e))?;
58
59 let address = IotaAddress::from(&pk);
60 Ok(KeyId::new(address.to_string()))
61 }
62
63 async fn sign(&self, key_id: &KeyId, data: &[u8], _pk_jwk: &Jwk) -> KeyStorageResult<Vec<u8>> {
64 let iota_address = key_id.as_str().parse().map_err(|_| {
65 KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_custom_message("invalid IOTA address")
66 })?;
67
68 self
69 .sign_raw(iota_address, data)
70 .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_source(e))
71 }
72
73 async fn delete(&self, _key_id: &KeyId) -> KeyStorageResult<()> {
74 Err(
75 KeyStorageError::new(KeyStorageErrorKind::Unspecified)
76 .with_custom_message("IOTA Keytool doesn't support key deletion"),
77 )
78 }
79
80 async fn exists(&self, key_id: &KeyId) -> KeyStorageResult<bool> {
81 let iota_address = key_id.as_str().parse().map_err(|_| {
82 KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_custom_message("invalid IOTA address")
83 })?;
84 let exists = self
85 .get_key(iota_address)
86 .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::RetryableIOFailure).with_source(e))?
87 .is_some();
88
89 Ok(exists)
90 }
91}
92
93fn check_key_alg_compatibility(key_type: &KeyType, alg: &JwsAlgorithm) -> KeyStorageResult<()> {
95 match (key_type.as_str(), alg) {
96 ("ed25519", JwsAlgorithm::EdDSA) => Ok(()),
97 ("secp256r1", JwsAlgorithm::ES256) => Ok(()),
98 ("secp256k1", JwsAlgorithm::ES256K) => Ok(()),
99 (key_type, alg) => Err(
100 KeyStorageError::new(crate::key_storage::KeyStorageErrorKind::KeyAlgorithmMismatch)
101 .with_custom_message(format!("`cannot use key type `{key_type}` with algorithm `{alg}`")),
102 ),
103 }
104}
105#[cfg(test)]
106mod tests {
107 use crate::JwkDocumentExt as _;
108 use crate::JwsSignatureOptions;
109 use crate::KeyIdStorage as _;
110 use crate::KeyType;
111 use crate::KeytoolStorage;
112 use crate::MethodDigest;
113 use anyhow::anyhow;
114 use identity_credential::credential::CredentialBuilder;
115 use identity_credential::credential::Subject;
116 use identity_credential::validator::FailFast;
117 use identity_credential::validator::JwtCredentialValidationOptions;
118 use identity_credential::validator::JwtCredentialValidator;
119 use identity_did::DID;
120 use identity_ecdsa_verifier::EcDSAJwsVerifier;
121 use identity_iota_core::IotaDocument;
122 use identity_verification::jws::JwsAlgorithm;
123 use identity_verification::MethodScope;
124 use iota_interaction::KeytoolStorage as Keytool;
125 use product_common::network_name::NetworkName;
126 use serde_json::Value;
127
128 fn make_storage() -> KeytoolStorage {
129 let keytool = Keytool::default();
130 KeytoolStorage::new(keytool.clone(), keytool)
131 }
132
133 #[tokio::test]
134 async fn keytool_storage_works() -> anyhow::Result<()> {
135 let storage = make_storage();
136
137 let mut did_doc = IotaDocument::new(&NetworkName::try_from("iota".to_string())?);
138 let fragment = did_doc
139 .generate_method(
140 &storage,
141 KeyType::from_static_str("secp256r1"),
142 JwsAlgorithm::ES256,
143 None,
144 MethodScope::VerificationMethod,
145 )
146 .await?;
147 let vm = did_doc.resolve_method(&fragment, None).expect("just created it");
148
149 let address_of_vm_key = storage
150 .key_id_storage()
151 .get_key_id(&MethodDigest::new(vm)?)
152 .await?
153 .as_str()
154 .parse()?;
155
156 let (_pk, alias) = storage
157 .key_storage()
158 .get_key(address_of_vm_key)?
159 .ok_or_else(|| anyhow!("something wrong with the new VM key!!"))?;
160
161 assert!(alias.starts_with("identity__"));
162
163 let credential = CredentialBuilder::new(Value::default())
164 .id("https://example.com/credentials/42".parse()?)
165 .issuer(did_doc.id().to_url())
166 .subject(Subject::with_id("https://example.com/users/123".parse()?))
167 .build()?;
168
169 let jwt = did_doc
170 .create_credential_jwt(&credential, &storage, &fragment, &JwsSignatureOptions::default(), None)
171 .await?;
172
173 let validator = JwtCredentialValidator::with_signature_verifier(EcDSAJwsVerifier::default());
174 validator.validate::<IotaDocument, Value>(
175 &jwt,
176 &did_doc,
177 &JwtCredentialValidationOptions::default(),
178 FailFast::FirstError,
179 )?;
180
181 Ok(())
182 }
183}