Skip to content

Commit fa0deb8

Browse files
authored
feat(keys): VerifyingKeys can be transcoded to/from SPKI DER/PEM (#273)
* feat(keys): SPKI conversion for VerifyingKeys * test(keys): Add unit test for SPKI PEM files * chore: incorporate changes in zkvm elfs * build: use rust 1.86 when building prism cli docker image
1 parent 6c94076 commit fa0deb8

File tree

6 files changed

+116
-47
lines changed

6 files changed

+116
-47
lines changed

Dockerfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM rust:1.83-slim-bookworm AS builder
1+
FROM rust:1.86-slim-bookworm AS builder
22

33
WORKDIR /usr/src/myapp
44

@@ -30,4 +30,3 @@ RUN apt-get update && apt-get install -y --no-install-recommends libssl3 && apt-
3030
COPY --from=builder /usr/src/myapp/target/release/prism-cli /usr/local/bin/prism-cli
3131

3232
ENTRYPOINT ["prism-cli"]
33-

crates/keys/src/tests.rs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,59 @@ mod key_tests {
5252

5353
#[test]
5454
fn test_reparsed_der_verifying_keys_are_equal_to_original() {
55-
// Not implemented for ec25519 / eip191 / cosmos_adr36 - skipping that
55+
let verifying_key_ed25519 = SigningKey::new_ed25519().verifying_key();
56+
let re_parsed_verifying_key =
57+
VerifyingKey::from_spki_der(&verifying_key_ed25519.to_spki_der().unwrap()).unwrap();
58+
assert_eq!(re_parsed_verifying_key, verifying_key_ed25519);
5659

5760
let verifying_key_secp256r1 = SigningKey::new_secp256r1().verifying_key();
58-
let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_der(
59-
verifying_key_secp256r1.algorithm(),
60-
&verifying_key_secp256r1.to_der().unwrap(),
61-
)
62-
.unwrap();
61+
let re_parsed_verifying_key =
62+
VerifyingKey::from_spki_der(&verifying_key_secp256r1.to_spki_der().unwrap()).unwrap();
6363
assert_eq!(re_parsed_verifying_key, verifying_key_secp256r1);
6464

6565
let verifying_key_secp256k1 = SigningKey::new_secp256k1().verifying_key();
66-
let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_der(
67-
verifying_key_secp256k1.algorithm(),
68-
&verifying_key_secp256k1.to_der().unwrap(),
69-
)
70-
.unwrap();
66+
let re_parsed_verifying_key =
67+
VerifyingKey::from_spki_der(&verifying_key_secp256k1.to_spki_der().unwrap()).unwrap();
68+
assert_eq!(re_parsed_verifying_key, verifying_key_secp256k1);
69+
70+
// Not implemented for eip191 / cosmos_adr36 - skipping those
71+
}
72+
73+
#[test]
74+
fn test_reparsed_verifying_keys_from_spki_pem_files_are_equal_to_original() {
75+
let temp_dir = env::temp_dir();
76+
77+
// Ed25519
78+
let verifying_key_ed25519 = SigningKey::new_ed25519().verifying_key();
79+
let spki_path = temp_dir.join("ed25519.pem");
80+
81+
verifying_key_ed25519.to_spki_pem_file(&spki_path).unwrap();
82+
let re_parsed_verifying_key = VerifyingKey::from_spki_pem_file(&spki_path).unwrap();
83+
84+
assert_eq!(re_parsed_verifying_key, verifying_key_ed25519);
85+
remove_file(&spki_path).unwrap();
86+
87+
// Secp256k1
88+
let verifying_key_secp256k1 = SigningKey::new_secp256k1().verifying_key();
89+
let spki_path = temp_dir.join("secp256k1.pem");
90+
91+
verifying_key_secp256k1.to_spki_pem_file(&spki_path).unwrap();
92+
let re_parsed_verifying_key = VerifyingKey::from_spki_pem_file(&spki_path).unwrap();
93+
7194
assert_eq!(re_parsed_verifying_key, verifying_key_secp256k1);
95+
remove_file(&spki_path).unwrap();
96+
97+
// Secp256r1
98+
let verifying_key_secp256r1 = SigningKey::new_secp256r1().verifying_key();
99+
let spki_path = temp_dir.join("secp256r1.pem");
100+
101+
verifying_key_secp256r1.to_spki_pem_file(&spki_path).unwrap();
102+
let re_parsed_verifying_key = VerifyingKey::from_spki_pem_file(&spki_path).unwrap();
103+
104+
assert_eq!(re_parsed_verifying_key, verifying_key_secp256r1);
105+
remove_file(&spki_path).unwrap();
106+
107+
// EIP-191 and Cosmos ADR-36 are using SECP256K1 keys and are omitted here
72108
}
73109

74110
#[test]

crates/keys/src/verifying_keys.rs

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
use alloy_primitives::eip191_hash_message;
22
use anyhow::{Result, anyhow, bail};
3+
use ed25519::PublicKeyBytes as Ed25519PublicKeyBytes;
34
use ed25519_consensus::VerificationKey as Ed25519VerifyingKey;
5+
use k256::ecdsa::VerifyingKey as Secp256k1VerifyingKey;
46
use p256::{
57
ecdsa::{
68
VerifyingKey as Secp256r1VerifyingKey,
79
signature::{DigestVerifier, hazmat::PrehashVerifier},
810
},
9-
pkcs8::{DecodePublicKey, EncodePublicKey},
11+
pkcs8::EncodePublicKey,
12+
};
13+
use pkcs8::{
14+
Document, LineEnding, SubjectPublicKeyInfoRef,
15+
der::{Decode, pem::PemLabel},
1016
};
11-
12-
use k256::ecdsa::VerifyingKey as Secp256k1VerifyingKey;
13-
1417
use serde::{Deserialize, Serialize};
1518
use sha2::Digest as _;
1619
use std::{
1720
self,
1821
borrow::Cow,
1922
hash::{Hash, Hasher},
23+
path::Path,
2024
};
2125
use utoipa::{
2226
PartialSchema, ToSchema,
@@ -84,19 +88,6 @@ impl VerifyingKey {
8488
}
8589
}
8690

87-
pub fn to_der(&self) -> Result<Vec<u8>> {
88-
let der = match self {
89-
VerifyingKey::Ed25519(_) => bail!("Ed25519 vk to DER format is not implemented"),
90-
VerifyingKey::Secp256k1(vk) => vk.to_public_key_der()?.into_vec(),
91-
VerifyingKey::Secp256r1(vk) => vk.to_public_key_der()?.into_vec(),
92-
VerifyingKey::Eip191(_) => bail!("EIP-191 vk to DER format is not implemented"),
93-
VerifyingKey::CosmosAdr36(_) => {
94-
bail!("Cosmos ADR-36 vk to DER format is not implemented")
95-
}
96-
};
97-
Ok(der)
98-
}
99-
10091
pub fn from_algorithm_and_bytes(algorithm: CryptoAlgorithm, bytes: &[u8]) -> Result<Self> {
10192
match algorithm {
10293
CryptoAlgorithm::Ed25519 => Ed25519VerifyingKey::try_from(bytes)
@@ -117,22 +108,6 @@ impl VerifyingKey {
117108
}
118109
}
119110

120-
pub fn from_algorithm_and_der(algorithm: CryptoAlgorithm, bytes: &[u8]) -> Result<Self> {
121-
match algorithm {
122-
CryptoAlgorithm::Ed25519 => bail!("Ed25519 vk from DER format is not implemented"),
123-
CryptoAlgorithm::Secp256k1 => Secp256k1VerifyingKey::from_public_key_der(bytes)
124-
.map(VerifyingKey::Secp256k1)
125-
.map_err(|e| e.into()),
126-
CryptoAlgorithm::Secp256r1 => Secp256r1VerifyingKey::from_public_key_der(bytes)
127-
.map(VerifyingKey::Secp256r1)
128-
.map_err(|e| e.into()),
129-
CryptoAlgorithm::Eip191 => bail!("Eth vk from DER format is not implemented"),
130-
CryptoAlgorithm::CosmosAdr36 => {
131-
bail!("Cosmos ADR-36 vk from DER format is not implemented")
132-
}
133-
}
134-
}
135-
136111
pub fn algorithm(&self) -> CryptoAlgorithm {
137112
match self {
138113
VerifyingKey::Ed25519(_) => CryptoAlgorithm::Ed25519,
@@ -191,6 +166,65 @@ impl VerifyingKey {
191166
}
192167
}
193168
}
169+
170+
fn to_spki_der_doc(&self) -> Result<Document> {
171+
match self {
172+
VerifyingKey::Ed25519(vk) => Ed25519PublicKeyBytes(vk.to_bytes()).to_public_key_der(),
173+
VerifyingKey::Secp256k1(vk) => vk.to_public_key_der(),
174+
VerifyingKey::Secp256r1(vk) => vk.to_public_key_der(),
175+
VerifyingKey::Eip191(_) => bail!("EIP-191 vk to DER format is not implemented"),
176+
VerifyingKey::CosmosAdr36(_) => {
177+
bail!("Cosmos ADR-36 vk to DER format is not implemented")
178+
}
179+
}
180+
.map_err(|_| anyhow!("Creating SPKI DER failed"))
181+
}
182+
183+
pub fn to_spki_der(&self) -> Result<Vec<u8>> {
184+
Ok(self.to_spki_der_doc()?.as_bytes().to_vec())
185+
}
186+
187+
pub fn to_spki_pem_file(&self, filename: impl AsRef<Path>) -> Result<()> {
188+
self.to_spki_der_doc()?
189+
.write_pem_file(filename, SubjectPublicKeyInfoRef::PEM_LABEL, LineEnding::LF)
190+
.map_err(|_| anyhow!("Creating PKCS8 PEM file failed"))
191+
}
192+
193+
fn from_spki(spki: SubjectPublicKeyInfoRef) -> Result<Self> {
194+
let algorithm = CryptoAlgorithm::try_from(spki.algorithm)?;
195+
196+
match algorithm {
197+
CryptoAlgorithm::Ed25519 => {
198+
let ed25519_spki = Ed25519PublicKeyBytes::try_from(spki)?;
199+
let ed25519_key = Ed25519VerifyingKey::try_from(ed25519_spki.as_ref() as &[u8])?;
200+
Ok(VerifyingKey::Ed25519(ed25519_key))
201+
}
202+
CryptoAlgorithm::Secp256k1 => {
203+
let secp256k1_key = Secp256k1VerifyingKey::try_from(spki)?;
204+
Ok(VerifyingKey::Secp256k1(secp256k1_key))
205+
}
206+
CryptoAlgorithm::Secp256r1 => {
207+
let secp256r1_key = Secp256r1VerifyingKey::try_from(spki)?;
208+
Ok(VerifyingKey::Secp256r1(secp256r1_key))
209+
}
210+
CryptoAlgorithm::Eip191 => bail!("Eth vk from DER format is not implemented"),
211+
CryptoAlgorithm::CosmosAdr36 => {
212+
bail!("Cosmos ADR-36 vk from DER format is not implemented")
213+
}
214+
}
215+
}
216+
217+
pub fn from_spki_der(bytes: &[u8]) -> Result<Self> {
218+
let spki = SubjectPublicKeyInfoRef::from_der(bytes)?;
219+
Self::from_spki(spki)
220+
}
221+
222+
pub fn from_spki_pem_file(filename: impl AsRef<Path>) -> Result<Self> {
223+
let (label, doc) = Document::read_pem_file(filename)?;
224+
SubjectPublicKeyInfoRef::validate_pem_label(&label)
225+
.map_err(|_| anyhow!("Incorrect PEM label"))?;
226+
Self::from_spki_der(doc.as_bytes())
227+
}
194228
}
195229

196230
impl TryFrom<CryptoPayload> for VerifyingKey {
2.52 KB
Binary file not shown.
2.52 KB
Binary file not shown.

verification_keys/keys.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"base_vk": "0x00d10bfc805af5f51d38ed75b18d8fba6a6f93f70a90516d3b9469b40516e018", "recursive_vk": "0x00cb3451d21dfe2ed596d52c91b8dc4397725257d499c195c2a3c8cb178a11bf"}
1+
{"base_vk": "0x00877c18096ae02a71b36540e570b0c765d0f457d4e96fc9cb86e7175333a0ce", "recursive_vk": "0x00efb9ffab32fc3309e5b4de61b3bdb30c1c9864bddedb6c4a897a649c862db2"}

0 commit comments

Comments
 (0)