1- use anyhow:: Context ;
1+ use anyhow:: { ensure , Context } ;
22use bitcoin:: secp256k1:: PublicKey ;
33use secrecy:: ExposeSecret ;
44use serde:: { Deserialize , Serialize } ;
@@ -21,6 +21,13 @@ pub struct ProvisionRequest {
2121 pub root_seed : RootSeed ,
2222}
2323
24+ #[ derive( Serialize , Deserialize ) ]
25+ pub struct NodeInstanceSeed {
26+ pub node : Node ,
27+ pub instance : Instance ,
28+ pub sealed_seed : SealedSeed ,
29+ }
30+
2431#[ derive( Serialize , Deserialize ) ]
2532pub struct Node {
2633 pub user_pk : UserPk ,
@@ -34,22 +41,39 @@ pub struct Instance {
3441}
3542
3643/// Uniquely identifies a sealed seed using its primary key fields.
37- #[ derive( Serialize , Deserialize ) ]
44+ #[ derive( Clone , Serialize , Deserialize ) ]
3845pub struct SealedSeedId {
3946 pub node_pk : PublicKey ,
4047 pub measurement : Measurement ,
4148 pub machine_id : MachineId ,
4249 pub min_cpusvn : MinCpusvn ,
4350}
4451
45- #[ derive( Serialize , Deserialize ) ]
52+ /// The user node's provisioned seed that is sealed and persisted using its
53+ /// platform enclave keys that are software and version specific.
54+ ///
55+ /// This struct is returned directly from the DB so it should be considered as
56+ /// untrusted and not-yet-validated. To validate and convert a [`SealedSeed`]
57+ /// into a [`RootSeed`], use [`unseal_and_validate`]. To encrypt an existing
58+ /// [`RootSeed`] into a [`SealedSeed`], use [`seal_from_root_seed`].
59+ ///
60+ /// See [`crate::enclave::seal`] for more implementation details.
61+ ///
62+ /// [`unseal_and_validate`]: Self::unseal_and_validate
63+ /// [`seal_from_root_seed`]: Self::seal_from_root_seed
64+ #[ derive( Clone , Serialize , Deserialize ) ]
4665pub struct SealedSeed {
4766 #[ serde( flatten) ]
4867 pub id : SealedSeedId ,
68+ /// The fully serialized + sealed root seed.
69+ // NOTE: This should probably be renamed to `raw` or `ciphertext` or smth
70+ // but that requires a DB migration
4971 pub seed : Vec < u8 > ,
5072}
5173
5274impl SealedSeed {
75+ const LABEL : & ' static [ u8 ] = b"sealed seed" ;
76+
5377 pub fn new (
5478 node_pk : PublicKey ,
5579 measurement : Measurement ,
@@ -67,38 +91,101 @@ impl SealedSeed {
6791 seed,
6892 }
6993 }
70- }
7194
72- /// The enclave's provisioned secrets that it will seal and persist using its
73- /// platform enclave keys that are software and version specific.
74- ///
75- /// See: [`crate::enclave::seal`]
76- // TODO(phlip9): rename this or SealedSeed?
77- pub struct ProvisionedSecrets {
78- pub root_seed : RootSeed ,
79- }
95+ pub fn seal_from_root_seed < R : Crng > (
96+ rng : & mut R ,
97+ root_seed : & RootSeed ,
98+ ) -> anyhow:: Result < Self > {
99+ // Construct the root seed ciphertext
100+ let root_seed_ref = root_seed. expose_secret ( ) . as_slice ( ) ;
101+ let sealed = enclave:: seal ( rng, Self :: LABEL , root_seed_ref. into ( ) )
102+ . context ( "Failed to seal root seed" ) ?;
103+ let sealed_bytes = sealed. serialize ( ) ;
80104
81- impl ProvisionedSecrets {
82- const LABEL : & ' static [ u8 ] = b"provisioned secrets" ;
105+ // Derive / compute the other fields
106+ let node_pk = root_seed. derive_node_pk ( rng) ;
107+ let measurement = enclave:: measurement ( ) ;
108+ let machine_id = enclave:: machine_id ( ) ;
109+ let min_cpusvn = enclave:: MIN_SGX_CPUSVN ;
83110
84- pub fn seal ( & self , rng : & mut dyn Crng ) -> anyhow:: Result < Sealed < ' _ > > {
85- let root_seed_ref = self . root_seed . expose_secret ( ) . as_slice ( ) ;
86- enclave:: seal ( rng, Self :: LABEL , root_seed_ref. into ( ) )
87- . context ( "Failed to seal provisioned secrets" )
111+ Ok ( Self :: new (
112+ node_pk,
113+ measurement,
114+ machine_id,
115+ min_cpusvn,
116+ sealed_bytes,
117+ ) )
88118 }
89119
90- pub fn unseal ( sealed : Sealed < ' _ > ) -> anyhow:: Result < Self > {
91- let bytes = enclave:: unseal ( Self :: LABEL , sealed)
120+ pub fn unseal_and_validate < R : Crng > (
121+ self ,
122+ rng : & mut R ,
123+ ) -> anyhow:: Result < RootSeed > {
124+ // Compute the SGX fields
125+ let measurement = enclave:: measurement ( ) ;
126+ let machine_id = enclave:: machine_id ( ) ;
127+ let min_cpusvn = enclave:: MIN_SGX_CPUSVN ;
128+
129+ // Validate SGX fields
130+ ensure ! (
131+ self . id. measurement == measurement,
132+ "Saved measurement doesn't match current measurement" ,
133+ ) ;
134+ ensure ! (
135+ self . id. machine_id == machine_id,
136+ "Saved machine id doesn't match current machine id" ,
137+ ) ;
138+ ensure ! (
139+ self . id. min_cpusvn == min_cpusvn,
140+ "Saved min CPUSVN doesn't match current min CPUSVN" ,
141+ ) ;
142+
143+ // Unseal
144+ let sealed = Sealed :: deserialize ( & self . seed )
145+ . context ( "Failed to deserialize sealed seed" ) ?;
146+ let unsealed_bytes = enclave:: unseal ( Self :: LABEL , sealed)
92147 . context ( "Failed to unseal provisioned secrets" ) ?;
93- let root_seed = RootSeed :: try_from ( bytes. as_slice ( ) )
148+
149+ // Reconstruct root seed
150+ let root_seed = RootSeed :: try_from ( unsealed_bytes. as_slice ( ) )
94151 . context ( "Failed to deserialize root seed" ) ?;
95- Ok ( Self { root_seed } )
152+
153+ // Validate node_pk
154+ let derived_node_pk = root_seed. derive_node_pk ( rng) ;
155+ ensure ! (
156+ self . id. node_pk == derived_node_pk,
157+ "Saved node pk doesn't match derived node pk"
158+ ) ;
159+
160+ // Validation complete, everything OK.
161+ Ok ( root_seed)
96162 }
97163}
98164
99- #[ derive( Serialize , Deserialize ) ]
100- pub struct NodeInstanceSeed {
101- pub node : Node ,
102- pub instance : Instance ,
103- pub sealed_seed : SealedSeed ,
165+ #[ cfg( test) ]
166+ mod test {
167+ use proptest:: arbitrary:: any;
168+ use proptest:: proptest;
169+ use secrecy:: ExposeSecret ;
170+
171+ use super :: * ;
172+ use crate :: rng:: SysRng ;
173+
174+ proptest ! {
175+ #[ test]
176+ fn seal_unseal_roundtrip( root_seed1 in any:: <RootSeed >( ) ) {
177+ let mut rng = SysRng :: new( ) ;
178+
179+ let root_seed2 =
180+ SealedSeed :: seal_from_root_seed( & mut rng, & root_seed1)
181+ . unwrap( )
182+ . unseal_and_validate( & mut rng)
183+ . unwrap( ) ;
184+
185+ assert_eq!(
186+ root_seed1. expose_secret( ) ,
187+ root_seed2. expose_secret( ) ,
188+ ) ;
189+ }
190+ }
104191}
0 commit comments