summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2025-10-28 14:10:48 -0400
committerOwen Jacobson <owen@grimoire.ca>2025-10-28 15:09:03 -0400
commit11f4f36a689b6447c9898a2840418e581cb3eb11 (patch)
treefc97b7aa36a20a5af58a692e814cbd9fea0348cc
parent4a91792e023a5877f8ac9b8a352e99c4486d698f (diff)
Use PKCS8 PEM, not raw SEC1 bytes, to store VAPID keys.
The `web-push` crate's VAPID signing support requires a private key. The `p256` crate is more than capable of generating one, but the easiest way to get a key from a `p256::ecdsa::SigningKey` to a `web_push::PartialVapidSignature` is via PKCS #8 PEM, not via the bytes. Since we'll need it in that form anyways, store it that way, so that we don't have to decode it using `p256`, re-encode to PEM, then decode to `PartialVapidSignature`. The migration in this commit invalidates existing VAPID keys. We could include support for re-encoding them on read, but there's little point: this code is still in flux anyways, and only development deployments exist. By the time this is final, the schema will have settled.
-rw-r--r--.sqlx/query-be295f56960d083d1f4c760f6acfdfbf9898a1a407e8a562a181542834d05eb0.json (renamed from .sqlx/query-edd16f1507f3b40270d652c7c204a4b9a518af418cc7e7fce9a6f0a106a6d66e.json)8
-rw-r--r--migrations/20251028173914_pem_vapid_keys.sql15
-rw-r--r--src/boot/app.rs2
-rw-r--r--src/event/app.rs2
-rw-r--r--src/vapid/app.rs5
-rw-r--r--src/vapid/repo.rs15
6 files changed, 35 insertions, 12 deletions
diff --git a/.sqlx/query-edd16f1507f3b40270d652c7c204a4b9a518af418cc7e7fce9a6f0a106a6d66e.json b/.sqlx/query-be295f56960d083d1f4c760f6acfdfbf9898a1a407e8a562a181542834d05eb0.json
index 2481fa9..ccec274 100644
--- a/.sqlx/query-edd16f1507f3b40270d652c7c204a4b9a518af418cc7e7fce9a6f0a106a6d66e.json
+++ b/.sqlx/query-be295f56960d083d1f4c760f6acfdfbf9898a1a407e8a562a181542834d05eb0.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n select\n key.changed_at as \"changed_at: DateTime\",\n key.changed_sequence as \"changed_sequence: Sequence\",\n signing.key as \"key: Vec<u8>\"\n from vapid_key as key\n join vapid_signing_key as signing\n ",
+ "query": "\n select\n key.changed_at as \"changed_at: DateTime\",\n key.changed_sequence as \"changed_sequence: Sequence\",\n signing.key\n from vapid_key as key\n join vapid_signing_key as signing\n ",
"describe": {
"columns": [
{
@@ -14,9 +14,9 @@
"type_info": "Integer"
},
{
- "name": "key: Vec<u8>",
+ "name": "key",
"ordinal": 2,
- "type_info": "Blob"
+ "type_info": "Text"
}
],
"parameters": {
@@ -28,5 +28,5 @@
false
]
},
- "hash": "edd16f1507f3b40270d652c7c204a4b9a518af418cc7e7fce9a6f0a106a6d66e"
+ "hash": "be295f56960d083d1f4c760f6acfdfbf9898a1a407e8a562a181542834d05eb0"
}
diff --git a/migrations/20251028173914_pem_vapid_keys.sql b/migrations/20251028173914_pem_vapid_keys.sql
new file mode 100644
index 0000000..6302504
--- /dev/null
+++ b/migrations/20251028173914_pem_vapid_keys.sql
@@ -0,0 +1,15 @@
+drop table vapid_signing_key;
+
+create table vapid_signing_key (
+ key text
+ not null
+);
+
+create unique index vapid_signing_key_singleton
+ on vapid_signing_key (0);
+
+-- Whatever key we had, if any, was just destroyed by dropping the table. Delete the metadata
+-- as well so that the server will issue a new one.
+delete
+from
+ vapid_key;
diff --git a/src/boot/app.rs b/src/boot/app.rs
index 8da3e90..88255b0 100644
--- a/src/boot/app.rs
+++ b/src/boot/app.rs
@@ -78,6 +78,7 @@ pub enum Error {
Database(#[from] sqlx::Error),
Name(#[from] name::Error),
Ecdsa(#[from] p256::ecdsa::Error),
+ Pkcs8(#[from] p256::pkcs8::Error),
}
impl From<user::repo::LoadError> for Error {
@@ -106,6 +107,7 @@ impl From<vapid::repo::Error> for Error {
match error {
Error::Database(error) => error.into(),
Error::Ecdsa(error) => error.into(),
+ Error::Pkcs8(error) => error.into(),
}
}
}
diff --git a/src/event/app.rs b/src/event/app.rs
index 6c657c7..1e471f1 100644
--- a/src/event/app.rs
+++ b/src/event/app.rs
@@ -98,6 +98,7 @@ pub enum Error {
Database(#[from] sqlx::Error),
Name(#[from] name::Error),
Ecdsa(#[from] p256::ecdsa::Error),
+ Pkcs8(#[from] p256::pkcs8::Error),
}
impl From<user::repo::LoadError> for Error {
@@ -126,6 +127,7 @@ impl From<vapid::repo::Error> for Error {
match error {
Error::Database(error) => error.into(),
Error::Ecdsa(error) => error.into(),
+ Error::Pkcs8(error) => error.into(),
}
}
}
diff --git a/src/vapid/app.rs b/src/vapid/app.rs
index 5814ba0..b6e1bc5 100644
--- a/src/vapid/app.rs
+++ b/src/vapid/app.rs
@@ -86,11 +86,11 @@ impl<'a> Vapid<'a> {
}
#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
pub enum Error {
- #[error(transparent)]
Database(#[from] sqlx::Error),
- #[error(transparent)]
Ecdsa(#[from] p256::ecdsa::Error),
+ Pkcs8(#[from] p256::pkcs8::Error),
}
impl From<repo::Error> for Error {
@@ -99,6 +99,7 @@ impl From<repo::Error> for Error {
match error {
Error::Database(error) => error.into(),
Error::Ecdsa(error) => error.into(),
+ Error::Pkcs8(error) => error.into(),
}
}
}
diff --git a/src/vapid/repo.rs b/src/vapid/repo.rs
index 4ac5286..98b3bae 100644
--- a/src/vapid/repo.rs
+++ b/src/vapid/repo.rs
@@ -1,4 +1,7 @@
-use p256::{NistP256, ecdsa::SigningKey, elliptic_curve::FieldBytes};
+use p256::{
+ ecdsa::SigningKey,
+ pkcs8::{DecodePrivateKey as _, EncodePrivateKey as _, LineEnding},
+};
use sqlx::{Sqlite, SqliteConnection, Transaction};
use super::{
@@ -76,8 +79,8 @@ impl Vapid<'_> {
}
pub async fn store_signing_key(&mut self, key: &SigningKey) -> Result<(), Error> {
- let key = key.to_bytes();
- let key = key.as_slice();
+ let key = key.to_pkcs8_pem(LineEnding::CRLF)?;
+ let key = key.as_str();
sqlx::query!(
r#"
insert into vapid_signing_key (key)
@@ -97,14 +100,13 @@ impl Vapid<'_> {
select
key.changed_at as "changed_at: DateTime",
key.changed_sequence as "changed_sequence: Sequence",
- signing.key as "key: Vec<u8>"
+ signing.key
from vapid_key as key
join vapid_signing_key as signing
"#
)
.map(|row| {
- let key = FieldBytes::<NistP256>::from_slice(&row.key);
- let key = SigningKey::from_bytes(key)?;
+ let key = SigningKey::from_pkcs8_pem(&row.key)?;
let key = key.verifying_key().to_owned();
let changed = Instant::new(row.changed_at, row.changed_sequence);
@@ -122,6 +124,7 @@ impl Vapid<'_> {
#[error(transparent)]
pub enum Error {
Ecdsa(#[from] p256::ecdsa::Error),
+ Pkcs8(#[from] p256::pkcs8::Error),
Database(#[from] sqlx::Error),
}