Merge changes Idbb3e029,I8b6136dc,I4c2bb1d8

* changes:
  Encrypt keys even when device locked
  Symmetric keyguard-bound superencryption
  ECDH encryption module
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 553746a..06b5598 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -138,6 +138,16 @@
                 check_keystore_permission(KeystorePerm::unlock())
                     .context("In on_lock_screen_event: Unlock with password.")?;
                 ENFORCEMENTS.set_device_locked(user_id, false);
+
+                DB.with(|db| {
+                    SUPER_KEY.unlock_screen_lock_bound_key(
+                        &mut db.borrow_mut(),
+                        user_id as u32,
+                        &password,
+                    )
+                })
+                .context("In on_lock_screen_event: unlock_screen_lock_bound_key failed")?;
+
                 // Unlock super key.
                 if let UserState::Uninitialized = DB
                     .with(|db| {
@@ -168,6 +178,8 @@
                 check_keystore_permission(KeystorePerm::lock())
                     .context("In on_lock_screen_event: Lock")?;
                 ENFORCEMENTS.set_device_locked(user_id, true);
+                SUPER_KEY.lock_screen_lock_bound_key(user_id as u32);
+
                 Ok(())
             }
             _ => {
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index e386735..21c9b74 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -68,7 +68,8 @@
         "--whitelist-function", "HKDFExpand",
         "--whitelist-function", "ECDHComputeKey",
         "--whitelist-function", "ECKEYGenerateKey",
-        "--whitelist-function", "ECKEYDeriveFromSecret",
+        "--whitelist-function", "ECKEYMarshalPrivateKey",
+        "--whitelist-function", "ECKEYParsePrivateKey",
         "--whitelist-function", "EC_KEY_get0_public_key",
         "--whitelist-function", "ECPOINTPoint2Oct",
         "--whitelist-function", "ECPOINTOct2Point",
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 2e613fd..e4a1ac3 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -236,10 +236,28 @@
     return key;
 }
 
-EC_KEY* ECKEYDeriveFromSecret(const uint8_t* secret, size_t secret_len) {
+size_t ECKEYMarshalPrivateKey(const EC_KEY* priv_key, uint8_t* buf, size_t len) {
+    CBB cbb;
+    size_t out_len;
+    if (!CBB_init_fixed(&cbb, buf, len) ||
+        !EC_KEY_marshal_private_key(&cbb, priv_key, EC_PKEY_NO_PARAMETERS | EC_PKEY_NO_PUBKEY) ||
+        !CBB_finish(&cbb, nullptr, &out_len)) {
+        return 0;
+    } else {
+        return out_len;
+    }
+}
+
+EC_KEY* ECKEYParsePrivateKey(const uint8_t* buf, size_t len) {
+    CBS cbs;
+    CBS_init(&cbs, buf, len);
     EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
-    auto result = EC_KEY_derive_from_secret(group, secret, secret_len);
+    auto result = EC_KEY_parse_private_key(&cbs, group);
     EC_GROUP_free(group);
+    if (result != nullptr && CBS_len(&cbs) != 0) {
+        EC_KEY_free(result);
+        return nullptr;
+    }
     return result;
 }
 
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index 6686c8c..f841eb3 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -55,7 +55,9 @@
 
   EC_KEY* ECKEYGenerateKey();
 
-  EC_KEY* ECKEYDeriveFromSecret(const uint8_t *secret, size_t secret_len);
+  size_t ECKEYMarshalPrivateKey(const EC_KEY *priv_key, uint8_t *buf, size_t len);
+
+  EC_KEY* ECKEYParsePrivateKey(const uint8_t *buf, size_t len);
 
   size_t ECPOINTPoint2Oct(const EC_POINT *point, uint8_t *buf, size_t len);
 
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index 1eec321..a369012 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -74,9 +74,13 @@
     #[error("Failed to generate key.")]
     ECKEYGenerateKeyFailed,
 
-    /// This is returned if the C implementation of ECKEYDeriveFromSecret returned null.
-    #[error("Failed to derive key.")]
-    ECKEYDeriveFailed,
+    /// This is returned if the C implementation of ECKEYMarshalPrivateKey returned 0.
+    #[error("Failed to marshal private key.")]
+    ECKEYMarshalPrivateKeyFailed,
+
+    /// This is returned if the C implementation of ECKEYParsePrivateKey returned null.
+    #[error("Failed to parse private key.")]
+    ECKEYParsePrivateKeyFailed,
 
     /// This is returned if the C implementation of ECPOINTPoint2Oct returned 0.
     #[error("Failed to convert point to oct.")]
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 98e6eef..3523a9d 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -20,9 +20,9 @@
 pub use error::Error;
 use keystore2_crypto_bindgen::{
     extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
-    AES_gcm_encrypt, ECDHComputeKey, ECKEYDeriveFromSecret, ECKEYGenerateKey, ECPOINTOct2Point,
-    ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key, EC_POINT_free, HKDFExpand, HKDFExtract,
-    EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
+    AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
+    ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key,
+    EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
 };
 use std::convert::TryFrom;
 use std::convert::TryInto;
@@ -338,14 +338,32 @@
     Ok(ECKey(key))
 }
 
-/// Calls the boringssl EC_KEY_derive_from_secret function.
-pub fn ec_key_derive_from_secret(secret: &[u8]) -> Result<ECKey, Error> {
-    // Safety: secret is a valid buffer.
-    let result = unsafe { ECKEYDeriveFromSecret(secret.as_ptr(), secret.len()) };
-    if result.is_null() {
-        return Err(Error::ECKEYDeriveFailed);
+/// Calls the boringssl EC_KEY_marshal_private_key function.
+pub fn ec_key_marshal_private_key(key: &ECKey) -> Result<ZVec, Error> {
+    let len = 39; // Empirically observed length of private key
+    let mut buf = ZVec::new(len)?;
+    // Safety: the key is valid.
+    // This will not write past the specified length of the buffer; if the
+    // len above is too short, it returns 0.
+    let written_len =
+        unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) } as usize;
+    if written_len == len {
+        Ok(buf)
+    } else {
+        Err(Error::ECKEYMarshalPrivateKeyFailed)
     }
-    Ok(ECKey(result))
+}
+
+/// Calls the boringssl EC_KEY_parse_private_key function.
+pub fn ec_key_parse_private_key(buf: &[u8]) -> Result<ECKey, Error> {
+    // Safety: this will not read past the specified length of the buffer.
+    // It fails if less than the whole buffer is consumed.
+    let key = unsafe { ECKEYParsePrivateKey(buf.as_ptr(), buf.len()) };
+    if key.is_null() {
+        Err(Error::ECKEYParsePrivateKeyFailed)
+    } else {
+        Ok(ECKey(key))
+    }
 }
 
 /// Calls the boringssl EC_KEY_get0_public_key function.
@@ -519,26 +537,26 @@
     }
 
     #[test]
-    fn test_ec() {
-        let key = ec_key_generate_key();
-        assert!(key.is_ok());
-        assert!(!key.unwrap().0.is_null());
+    fn test_ec() -> Result<(), Error> {
+        let priv0 = ec_key_generate_key()?;
+        assert!(!priv0.0.is_null());
+        let pub0 = ec_key_get0_public_key(&priv0);
 
-        let key = ec_key_derive_from_secret(&[42; 16]);
-        assert!(key.is_ok());
-        let key = key.unwrap();
-        assert!(!key.0.is_null());
+        let priv1 = ec_key_generate_key()?;
+        let pub1 = ec_key_get0_public_key(&priv1);
 
-        let point = ec_key_get0_public_key(&key);
+        let priv0s = ec_key_marshal_private_key(&priv0)?;
+        let pub0s = ec_point_point_to_oct(pub0.get_point())?;
+        let pub1s = ec_point_point_to_oct(pub1.get_point())?;
 
-        let result = ecdh_compute_key(point.get_point(), &key);
-        assert!(result.is_ok());
+        let priv0 = ec_key_parse_private_key(&priv0s)?;
+        let pub0 = ec_point_oct_to_point(&pub0s)?;
+        let pub1 = ec_point_oct_to_point(&pub1s)?;
 
-        let oct = ec_point_point_to_oct(point.get_point());
-        assert!(oct.is_ok());
-        let oct = oct.unwrap();
+        let left_key = ecdh_compute_key(pub0.get_point(), &priv1)?;
+        let right_key = ecdh_compute_key(pub1.get_point(), &priv0)?;
 
-        let point2 = ec_point_oct_to_point(oct.as_slice());
-        assert!(point2.is_ok());
+        assert_eq!(left_key, right_key);
+        Ok(())
     }
 }
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 5f35444..7e260fe 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -41,7 +41,6 @@
 //! from the database module these functions take permission check
 //! callbacks.
 
-use crate::error::{Error as KsError, ErrorCode, ResponseCode};
 use crate::impl_metadata; // This is in db_utils.rs
 use crate::key_parameter::{KeyParameter, Tag};
 use crate::permission::KeyPermSet;
@@ -49,6 +48,11 @@
 use crate::{
     db_utils::{self, SqlField},
     gc::Gc,
+    super_key::USER_SUPER_KEY,
+};
+use crate::{
+    error::{Error as KsError, ErrorCode, ResponseCode},
+    super_key::SuperKeyType,
 };
 use anyhow::{anyhow, Context, Result};
 use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
@@ -108,6 +112,8 @@
         /// Vector representing the raw public key so results from the server can be matched
         /// to the right entry
         AttestationRawPubKey(Vec<u8>) with accessor attestation_raw_pub_key,
+        /// SEC1 public key for ECDH encryption
+        Sec1PublicKey(Vec<u8>) with accessor sec1_public_key,
         //  --- ADD NEW META DATA FIELDS HERE ---
         // For backwards compatibility add new entries only to
         // end of this list and above this comment.
@@ -178,6 +184,8 @@
         AeadTag(Vec<u8>) with accessor aead_tag,
         /// The uuid of the owning KeyMint instance.
         KmUuid(Uuid) with accessor km_uuid,
+        /// If the key is ECDH encrypted, this is the ephemeral public key
+        PublicKey(Vec<u8>) with accessor public_key,
         //  --- ADD NEW META DATA FIELDS HERE ---
         // For backwards compatibility add new entries only to
         // end of this list and above this comment.
@@ -816,9 +824,6 @@
     const UNASSIGNED_KEY_ID: i64 = -1i64;
     const PERBOOT_DB_FILE_NAME: &'static str = &"file:perboot.sqlite?mode=memory&cache=shared";
 
-    /// The alias of the user super key.
-    pub const USER_SUPER_KEY_ALIAS: &'static str = &"USER_SUPER_KEY";
-
     /// This creates a PerBootDbKeepAlive object to keep the per boot database alive.
     pub fn keep_perboot_db_alive() -> Result<PerBootDbKeepAlive> {
         let conn = Connection::open_in_memory()
@@ -1149,7 +1154,10 @@
     pub fn store_super_key(
         &mut self,
         user_id: u32,
-        blob_info: &(&[u8], &BlobMetaData),
+        key_type: &SuperKeyType,
+        blob: &[u8],
+        blob_metadata: &BlobMetaData,
+        key_metadata: &KeyMetaData,
     ) -> Result<KeyEntry> {
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_id = Self::insert_with_retry(|id| {
@@ -1162,7 +1170,7 @@
                         KeyType::Super,
                         Domain::APP.0,
                         user_id as i64,
-                        Self::USER_SUPER_KEY_ALIAS,
+                        key_type.alias,
                         KeyLifeCycle::Live,
                         &KEYSTORE_UUID,
                     ],
@@ -1170,7 +1178,8 @@
             })
             .context("Failed to insert into keyentry table.")?;
 
-            let (blob, blob_metadata) = *blob_info;
+            key_metadata.store_in_db(key_id, tx).context("KeyMetaData::store_in_db failed")?;
+
             Self::set_blob_internal(
                 &tx,
                 key_id,
@@ -1188,12 +1197,16 @@
     }
 
     /// Loads super key of a given user, if exists
-    pub fn load_super_key(&mut self, user_id: u32) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+    pub fn load_super_key(
+        &mut self,
+        key_type: &SuperKeyType,
+        user_id: u32,
+    ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
         self.with_transaction(TransactionBehavior::Immediate, |tx| {
             let key_descriptor = KeyDescriptor {
                 domain: Domain::APP,
                 nspace: user_id as i64,
-                alias: Some(String::from("USER_SUPER_KEY")),
+                alias: Some(key_type.alias.into()),
                 blob: None,
             };
             let id = Self::load_key_entry_id(&tx, &key_descriptor, KeyType::Super);
@@ -1289,7 +1302,7 @@
                         Some(&blob),
                         Some(&metadata),
                     )
-                    .context("In get_of_create_key_with.")?;
+                    .context("In get_or_create_key_with.")?;
                     (
                         id,
                         KeyEntry {
@@ -2649,7 +2662,7 @@
                     // OR super key:
                     KeyType::Super,
                     user_id,
-                    Self::USER_SUPER_KEY_ALIAS,
+                    USER_SUPER_KEY.alias,
                     KeyLifeCycle::Live
                 ])
                 .context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
@@ -4887,29 +4900,34 @@
         let mut db = new_test_db()?;
         let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
         let super_key = keystore2_crypto::generate_aes256_key()?;
-        let secret = String::from("keystore2 is great.");
-        let secret_bytes = secret.into_bytes();
+        let secret_bytes = b"keystore2 is great.";
         let (encrypted_secret, iv, tag) =
-            keystore2_crypto::aes_gcm_encrypt(&secret_bytes, &super_key)?;
+            keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
 
         let (encrypted_super_key, metadata) =
             SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
-        db.store_super_key(1, &(&encrypted_super_key, &metadata))?;
+        db.store_super_key(
+            1,
+            &USER_SUPER_KEY,
+            &encrypted_super_key,
+            &metadata,
+            &KeyMetaData::new(),
+        )?;
 
         //check if super key exists
-        assert!(db.key_exists(Domain::APP, 1, "USER_SUPER_KEY", KeyType::Super)?);
+        assert!(db.key_exists(Domain::APP, 1, &USER_SUPER_KEY.alias, KeyType::Super)?);
 
-        let (_, key_entry) = db.load_super_key(1)?.unwrap();
-        let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(key_entry, &pw)?;
-
-        let decrypted_secret_bytes = keystore2_crypto::aes_gcm_decrypt(
-            &encrypted_secret,
-            &iv,
-            &tag,
-            &loaded_super_key.get_key(),
+        let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
+        let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
+            USER_SUPER_KEY.algorithm,
+            key_entry,
+            &pw,
+            None,
         )?;
-        let decrypted_secret = String::from_utf8((&decrypted_secret_bytes).to_vec())?;
-        assert_eq!(String::from("keystore2 is great."), decrypted_secret);
+
+        let decrypted_secret_bytes =
+            loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
+        assert_eq!(secret_bytes, &*decrypted_secret_bytes);
         Ok(())
     }
 }
diff --git a/keystore2/src/ec_crypto.rs b/keystore2/src/ec_crypto.rs
new file mode 100644
index 0000000..0425d4a
--- /dev/null
+++ b/keystore2/src/ec_crypto.rs
@@ -0,0 +1,137 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Implement ECDH-based encryption.
+
+use anyhow::{Context, Result};
+use keystore2_crypto::{
+    aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
+    ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
+    ec_point_point_to_oct, ecdh_compute_key, generate_salt, hkdf_expand, hkdf_extract, ECKey, ZVec,
+    AES_256_KEY_LENGTH,
+};
+
+/// Private key for ECDH encryption.
+pub struct ECDHPrivateKey(ECKey);
+
+impl ECDHPrivateKey {
+    /// Randomly generate a fresh keypair.
+    pub fn generate() -> Result<ECDHPrivateKey> {
+        ec_key_generate_key()
+            .map(ECDHPrivateKey)
+            .context("In ECDHPrivateKey::generate: generation failed")
+    }
+
+    /// Deserialize bytes into an ECDH keypair
+    pub fn from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey> {
+        ec_key_parse_private_key(buf)
+            .map(ECDHPrivateKey)
+            .context("In ECDHPrivateKey::from_private_key: parsing failed")
+    }
+
+    /// Serialize the ECDH key into bytes
+    pub fn private_key(&self) -> Result<ZVec> {
+        ec_key_marshal_private_key(&self.0)
+            .context("In ECDHPrivateKey::private_key: marshalling failed")
+    }
+
+    /// Generate the serialization of the corresponding public key
+    pub fn public_key(&self) -> Result<Vec<u8>> {
+        let point = ec_key_get0_public_key(&self.0);
+        ec_point_point_to_oct(point.get_point())
+            .context("In ECDHPrivateKey::public_key: marshalling failed")
+    }
+
+    /// Use ECDH to agree an AES key with another party whose public key we have.
+    /// Sender and recipient public keys are passed separately because they are
+    /// switched in encryption vs decryption.
+    fn agree_key(
+        &self,
+        salt: &[u8],
+        other_public_key: &[u8],
+        sender_public_key: &[u8],
+        recipient_public_key: &[u8],
+    ) -> Result<ZVec> {
+        let hkdf = hkdf_extract(sender_public_key, salt)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on sender_public_key failed")?;
+        let hkdf = hkdf_extract(recipient_public_key, &hkdf)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on recipient_public_key failed")?;
+        let other_public_key = ec_point_oct_to_point(other_public_key)
+            .context("In ECDHPrivateKey::agree_key: ec_point_oct_to_point failed")?;
+        let secret = ecdh_compute_key(other_public_key.get_point(), &self.0)
+            .context("In ECDHPrivateKey::agree_key: ecdh_compute_key failed")?;
+        let prk = hkdf_extract(&secret, &hkdf)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on secret failed")?;
+
+        let aes_key = hkdf_expand(AES_256_KEY_LENGTH, &prk, b"AES-256-GCM key")
+            .context("In ECDHPrivateKey::agree_key: hkdf_expand failed")?;
+        Ok(aes_key)
+    }
+
+    /// Encrypt a message to the party with the given public key
+    pub fn encrypt_message(
+        recipient_public_key: &[u8],
+        message: &[u8],
+    ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)> {
+        let sender_key =
+            Self::generate().context("In ECDHPrivateKey::encrypt_message: generate failed")?;
+        let sender_public_key = sender_key
+            .public_key()
+            .context("In ECDHPrivateKey::encrypt_message: public_key failed")?;
+        let salt =
+            generate_salt().context("In ECDHPrivateKey::encrypt_message: generate_salt failed")?;
+        let aes_key = sender_key
+            .agree_key(&salt, recipient_public_key, &sender_public_key, recipient_public_key)
+            .context("In ECDHPrivateKey::encrypt_message: agree_key failed")?;
+        let (ciphertext, iv, tag) = aes_gcm_encrypt(message, &aes_key)
+            .context("In ECDHPrivateKey::encrypt_message: aes_gcm_encrypt failed")?;
+        Ok((sender_public_key, salt, iv, ciphertext, tag))
+    }
+
+    /// Decrypt a message sent to us
+    pub fn decrypt_message(
+        &self,
+        sender_public_key: &[u8],
+        salt: &[u8],
+        iv: &[u8],
+        ciphertext: &[u8],
+        tag: &[u8],
+    ) -> Result<ZVec> {
+        let recipient_public_key = self.public_key()?;
+        let aes_key = self
+            .agree_key(salt, sender_public_key, sender_public_key, &recipient_public_key)
+            .context("In ECDHPrivateKey::decrypt_message: agree_key failed")?;
+        aes_gcm_decrypt(ciphertext, iv, tag, &aes_key)
+            .context("In ECDHPrivateKey::decrypt_message: aes_gcm_decrypt failed")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_crypto_roundtrip() -> Result<()> {
+        let message = b"Hello world";
+        let recipient = ECDHPrivateKey::generate()?;
+        let (sender_public_key, salt, iv, ciphertext, tag) =
+            ECDHPrivateKey::encrypt_message(&recipient.public_key()?, message)?;
+        let recipient = ECDHPrivateKey::from_private_key(&recipient.private_key()?)?;
+        let decrypted =
+            recipient.decrypt_message(&sender_public_key, &salt, &iv, &ciphertext, &tag)?;
+        let dc: &[u8] = &decrypted;
+        assert_eq!(message, dc);
+        Ok(())
+    }
+}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 2cc704b..3f003be 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -14,11 +14,11 @@
 
 //! This is the Keystore 2.0 Enforcements module.
 // TODO: more description to follow.
-use crate::authorization::Error as AuthzError;
 use crate::database::{AuthTokenEntry, MonotonicRawTime};
 use crate::error::{map_binder_status, Error, ErrorCode};
 use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
 use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::{authorization::Error as AuthzError, super_key::SuperEncryptionType};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
     HardwareAuthenticatorType::HardwareAuthenticatorType,
@@ -29,7 +29,7 @@
 };
 use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
 use android_system_keystore2::aidl::android::system::keystore2::{
-    IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
+    Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
     OperationChallenge::OperationChallenge,
 };
 use android_system_keystore2::binder::Strong;
@@ -757,16 +757,32 @@
     }
 
     /// Given the set of key parameters and flags, check if super encryption is required.
-    pub fn super_encryption_required(key_parameters: &[KeyParameter], flags: Option<i32>) -> bool {
-        let auth_bound = key_parameters.iter().any(|kp| kp.get_tag() == Tag::USER_SECURE_ID);
-
-        let skip_lskf_binding = if let Some(flags) = flags {
-            (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0
-        } else {
-            false
-        };
-
-        auth_bound && !skip_lskf_binding
+    pub fn super_encryption_required(
+        domain: &Domain,
+        key_parameters: &[KeyParameter],
+        flags: Option<i32>,
+    ) -> SuperEncryptionType {
+        if *domain != Domain::APP {
+            return SuperEncryptionType::None;
+        }
+        if let Some(flags) = flags {
+            if (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0 {
+                return SuperEncryptionType::None;
+            }
+        }
+        if key_parameters
+            .iter()
+            .any(|kp| matches!(kp.key_parameter_value(), KeyParameterValue::UnlockedDeviceRequired))
+        {
+            return SuperEncryptionType::ScreenLockBound;
+        }
+        if key_parameters
+            .iter()
+            .any(|kp| matches!(kp.key_parameter_value(), KeyParameterValue::UserSecureID(_)))
+        {
+            return SuperEncryptionType::LskfBound;
+        }
+        SuperEncryptionType::None
     }
 
     /// Finds a matching auth token along with a timestamp token.
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 6f2cdd0..423f507 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -965,8 +965,7 @@
                             let decrypted = match key_manager
                                 .get_per_boot_key_by_user_id(uid_to_android_user(uid))
                             {
-                                Some(key) => aes_gcm_decrypt(data, iv, tag, &(key.get_key()))
-                                    .context(
+                                Some(key) => key.aes_gcm_decrypt(data, iv, tag).context(
                                     "In load_by_uid_alias: while trying to decrypt legacy blob.",
                                 )?,
                                 None => {
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 7567070..e5bcae4 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -14,15 +14,18 @@
 
 //! This module acts as a bridge between the legacy key database and the keystore2 database.
 
-use crate::database::{
-    BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData, KeyMetaEntry,
-    KeystoreDB, Uuid, KEYSTORE_UUID,
-};
 use crate::error::Error;
 use crate::key_parameter::KeyParameterValue;
 use crate::legacy_blob::BlobValue;
 use crate::utils::uid_to_android_user;
 use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use crate::{
+    database::{
+        BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
+        KeyMetaEntry, KeystoreDB, Uuid, KEYSTORE_UUID,
+    },
+    super_key::USER_SUPER_KEY,
+};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
@@ -450,7 +453,7 @@
 
                         let super_key_id = match self
                             .db
-                            .load_super_key(user_id)
+                            .load_super_key(&USER_SUPER_KEY, user_id)
                             .context("In check_and_migrate: Failed to load super key")?
                         {
                             Some((_, entry)) => entry.id(),
@@ -560,10 +563,18 @@
                 crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
                     .context("In check_and_migrate_super_key: Trying to encrypt super key.")?;
 
-            self.db.store_super_key(user_id, &(&blob, &blob_metadata)).context(concat!(
-                "In check_and_migrate_super_key: ",
-                "Trying to insert legacy super_key into the database."
-            ))?;
+            self.db
+                .store_super_key(
+                    user_id,
+                    &USER_SUPER_KEY,
+                    &blob,
+                    &blob_metadata,
+                    &KeyMetaData::new(),
+                )
+                .context(concat!(
+                    "In check_and_migrate_super_key: ",
+                    "Trying to insert legacy super_key into the database."
+                ))?;
             self.legacy_loader.remove_super_key(user_id);
             self.recently_migrated_super_key.insert(user_id);
             Ok(())
@@ -602,7 +613,7 @@
 
         let super_key_id = self
             .db
-            .load_super_key(user_id)
+            .load_super_key(&USER_SUPER_KEY, user_id)
             .context("In bulk_delete: Failed to load super key")?
             .map(|(_, entry)| entry.id());
 
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index cb47e3e..97b0bed 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -19,6 +19,7 @@
 pub mod async_task;
 pub mod authorization;
 pub mod database;
+pub mod ec_crypto;
 pub mod enforcements;
 pub mod entropy;
 pub mod error;
@@ -34,6 +35,7 @@
 pub mod security_level;
 pub mod service;
 pub mod shared_secret_negotiation;
+pub mod try_insert;
 pub mod utils;
 
 mod attestation_key_utils;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 47bd946..e059a0b 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -53,6 +53,13 @@
         check_keystore_permission(KeystorePerm::change_password())
             .context("In on_user_password_changed.")?;
 
+        if let Some(pw) = password.as_ref() {
+            DB.with(|db| {
+                SUPER_KEY.unlock_screen_lock_bound_key(&mut db.borrow_mut(), user_id as u32, pw)
+            })
+            .context("In on_user_password_changed: unlock_screen_lock_bound_key failed")?;
+        }
+
         match DB
             .with(|db| {
                 UserState::get_with_password_changed(
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index e50155b..f7bf1c6 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -300,7 +300,8 @@
             .upgrade_keyblob_if_required_with(
                 &*km_dev,
                 key_id_guard,
-                &(&km_blob, &blob_metadata),
+                &km_blob,
+                &blob_metadata,
                 &operation_parameters,
                 |blob| loop {
                     match map_km_error(km_dev.begin(
@@ -462,7 +463,8 @@
                 .upgrade_keyblob_if_required_with(
                     &*km_dev,
                     Some(key_id_guard),
-                    &(&KeyBlob::Ref(&blob), &blob_metadata),
+                    &KeyBlob::Ref(&blob),
+                    &blob_metadata,
                     &params,
                     |blob| {
                         let attest_key = Some(AttestationKey {
@@ -651,7 +653,8 @@
             .upgrade_keyblob_if_required_with(
                 &*km_dev,
                 Some(wrapping_key_id_guard),
-                &(&wrapping_key_blob, &wrapping_blob_metadata),
+                &wrapping_key_blob,
+                &wrapping_blob_metadata,
                 &[],
                 |wrapping_blob| {
                     let creation_result = map_km_error(km_dev.importWrappedKey(
@@ -671,48 +674,62 @@
             .context("In import_wrapped_key: Trying to store the new key.")
     }
 
+    fn store_upgraded_keyblob(
+        key_id_guard: KeyIdGuard,
+        km_uuid: Option<&Uuid>,
+        key_blob: &KeyBlob,
+        upgraded_blob: &[u8],
+    ) -> Result<()> {
+        let (upgraded_blob_to_be_stored, new_blob_metadata) =
+            SuperKeyManager::reencrypt_if_required(key_blob, &upgraded_blob)
+                .context("In store_upgraded_keyblob: Failed to handle super encryption.")?;
+
+        let mut new_blob_metadata = new_blob_metadata.unwrap_or_else(BlobMetaData::new);
+        if let Some(uuid) = km_uuid {
+            new_blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
+        }
+
+        DB.with(|db| {
+            let mut db = db.borrow_mut();
+            db.set_blob(
+                &key_id_guard,
+                SubComponentType::KEY_BLOB,
+                Some(&upgraded_blob_to_be_stored),
+                Some(&new_blob_metadata),
+            )
+        })
+        .context("In store_upgraded_keyblob: Failed to insert upgraded blob into the database.")
+    }
+
     fn upgrade_keyblob_if_required_with<T, F>(
         &self,
         km_dev: &dyn IKeyMintDevice,
         key_id_guard: Option<KeyIdGuard>,
-        blob_info: &(&KeyBlob, &BlobMetaData),
+        key_blob: &KeyBlob,
+        blob_metadata: &BlobMetaData,
         params: &[KeyParameter],
         f: F,
     ) -> Result<(T, Option<Vec<u8>>)>
     where
         F: Fn(&[u8]) -> Result<T, Error>,
     {
-        match f(blob_info.0) {
+        match f(key_blob) {
             Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
-                let upgraded_blob = map_km_error(km_dev.upgradeKey(blob_info.0, params))
+                let upgraded_blob = map_km_error(km_dev.upgradeKey(key_blob, params))
                     .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
 
-                let (upgraded_blob_to_be_stored, blob_metadata) =
-                    SuperKeyManager::reencrypt_on_upgrade_if_required(blob_info.0, &upgraded_blob)
-                        .context(
-                        "In upgrade_keyblob_if_required_with: Failed to handle super encryption.",
+                if let Some(kid) = key_id_guard {
+                    Self::store_upgraded_keyblob(
+                        kid,
+                        blob_metadata.km_uuid(),
+                        key_blob,
+                        &upgraded_blob,
+                    )
+                    .context(
+                        "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
                     )?;
-
-                let mut blob_metadata = blob_metadata.unwrap_or_else(BlobMetaData::new);
-                if let Some(uuid) = blob_info.1.km_uuid() {
-                    blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
                 }
 
-                key_id_guard.map_or(Ok(()), |key_id_guard| {
-                    DB.with(|db| {
-                        let mut db = db.borrow_mut();
-                        db.set_blob(
-                            &key_id_guard,
-                            SubComponentType::KEY_BLOB,
-                            Some(&upgraded_blob_to_be_stored),
-                            Some(&blob_metadata),
-                        )
-                    })
-                    .context(concat!(
-                        "In upgrade_keyblob_if_required_with: ",
-                        "Failed to insert upgraded blob into the database.",
-                    ))
-                })?;
                 match f(&upgraded_blob) {
                     Ok(v) => Ok((v, Some(upgraded_blob))),
                     Err(e) => Err(e).context(concat!(
@@ -721,10 +738,25 @@
                     )),
                 }
             }
-            Err(e) => {
-                Err(e).context("In upgrade_keyblob_if_required_with: Failed perform operation.")
+            result => {
+                if let Some(kid) = key_id_guard {
+                    if key_blob.force_reencrypt() {
+                        Self::store_upgraded_keyblob(
+                            kid,
+                            blob_metadata.km_uuid(),
+                            key_blob,
+                            key_blob,
+                        )
+                        .context(concat!(
+                            "In upgrade_keyblob_if_required_with: ",
+                            "store_upgraded_keyblob failed in forced reencrypt"
+                        ))?;
+                    }
+                }
+                result
+                    .map(|v| (v, None))
+                    .context("In upgrade_keyblob_if_required_with: Called closure failed.")
             }
-            Ok(v) => Ok((v, None)),
         }
     }
 
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index aa434d6..655e0ce 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -15,10 +15,20 @@
 #![allow(dead_code)]
 
 use crate::{
-    database::BlobMetaData, database::BlobMetaEntry, database::EncryptedBy, database::KeyEntry,
-    database::KeyType, database::KeystoreDB, enforcements::Enforcements, error::Error,
-    error::ResponseCode, key_parameter::KeyParameter, legacy_blob::LegacyBlobLoader,
+    database::BlobMetaData,
+    database::BlobMetaEntry,
+    database::EncryptedBy,
+    database::KeyEntry,
+    database::KeyType,
+    database::{KeyMetaData, KeyMetaEntry, KeystoreDB},
+    ec_crypto::ECDHPrivateKey,
+    enforcements::Enforcements,
+    error::Error,
+    error::ResponseCode,
+    key_parameter::KeyParameter,
+    legacy_blob::LegacyBlobLoader,
     legacy_migrator::LegacyMigrator,
+    try_insert::TryInsert,
 };
 use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
 use anyhow::{Context, Result};
@@ -35,6 +45,55 @@
 
 type UserId = u32;
 
+/// Encryption algorithm used by a particular type of superencryption key
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum SuperEncryptionAlgorithm {
+    /// Symmetric encryption with AES-256-GCM
+    Aes256Gcm,
+    /// Public-key encryption with ECDH P-256
+    EcdhP256,
+}
+
+/// A particular user may have several superencryption keys in the database, each for a
+/// different purpose, distinguished by alias. Each is associated with a static
+/// constant of this type.
+pub struct SuperKeyType {
+    /// Alias used to look the key up in the `persistent.keyentry` table.
+    pub alias: &'static str,
+    /// Encryption algorithm
+    pub algorithm: SuperEncryptionAlgorithm,
+}
+
+/// Key used for LskfLocked keys; the corresponding superencryption key is loaded in memory
+/// when the user first unlocks, and remains in memory until the device reboots.
+pub const USER_SUPER_KEY: SuperKeyType =
+    SuperKeyType { alias: "USER_SUPER_KEY", algorithm: SuperEncryptionAlgorithm::Aes256Gcm };
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Symmetric.
+pub const USER_SCREEN_LOCK_BOUND_KEY: SuperKeyType = SuperKeyType {
+    alias: "USER_SCREEN_LOCK_BOUND_KEY",
+    algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+};
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Asymmetric, so keys can be encrypted when the device is locked.
+pub const USER_SCREEN_LOCK_BOUND_ECDH_KEY: SuperKeyType = SuperKeyType {
+    alias: "USER_SCREEN_LOCK_BOUND_ECDH_KEY",
+    algorithm: SuperEncryptionAlgorithm::EcdhP256,
+};
+
+/// Superencryption to apply to a new key.
+#[derive(Debug, Clone, Copy)]
+pub enum SuperEncryptionType {
+    /// Do not superencrypt this key.
+    None,
+    /// Superencrypt with a key that remains in memory from first unlock to reboot.
+    LskfBound,
+    /// Superencrypt with a key cleared from memory when the device is locked.
+    ScreenLockBound,
+}
+
 #[derive(Default)]
 struct UserSuperKeys {
     /// The per boot key is used for LSKF binding of authentication bound keys. There is one
@@ -42,23 +101,36 @@
     /// secret, that is itself derived from the user's lock screen knowledge factor (LSKF).
     /// When the user unlocks the device for the first time, this key is unlocked, i.e., decrypted,
     /// and stays memory resident until the device reboots.
-    per_boot: Option<SuperKey>,
+    per_boot: Option<Arc<SuperKey>>,
     /// The screen lock key works like the per boot key with the distinction that it is cleared
     /// from memory when the screen lock is engaged.
-    /// TODO the life cycle is not fully implemented at this time.
-    screen_lock: Option<Arc<ZVec>>,
+    screen_lock_bound: Option<Arc<SuperKey>>,
+    /// When the device is locked, screen-lock-bound keys can still be encrypted, using
+    /// ECDH public-key encryption. This field holds the decryption private key.
+    screen_lock_bound_private: Option<Arc<SuperKey>>,
 }
 
-#[derive(Default, Clone)]
 pub struct SuperKey {
-    key: Arc<ZVec>,
+    algorithm: SuperEncryptionAlgorithm,
+    key: ZVec,
     // id of the super key in the database.
     id: i64,
+    /// ECDH is more expensive than AES. So on ECDH private keys we set the
+    /// reencrypt_with field to point at the corresponding AES key, and the
+    /// keys will be re-encrypted with AES on first use.
+    reencrypt_with: Option<Arc<SuperKey>>,
 }
 
 impl SuperKey {
-    pub fn get_key(&self) -> &Arc<ZVec> {
-        &self.key
+    /// For most purposes `unwrap_key` handles decryption,
+    /// but legacy handling and some tests need to assume AES and decrypt directly.
+    pub fn aes_gcm_decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+        if self.algorithm == SuperEncryptionAlgorithm::Aes256Gcm {
+            aes_gcm_decrypt(data, iv, tag, &self.key)
+                .context("In aes_gcm_decrypt: decryption failed")
+        } else {
+            Err(Error::sys()).context("In aes_gcm_decrypt: Key is not an AES key")
+        }
     }
 
     pub fn get_id(&self) -> i64 {
@@ -69,7 +141,13 @@
 #[derive(Default)]
 struct SkmState {
     user_keys: HashMap<UserId, UserSuperKeys>,
-    key_index: HashMap<i64, Weak<ZVec>>,
+    key_index: HashMap<i64, Weak<SuperKey>>,
+}
+
+impl SkmState {
+    fn add_key_to_key_index(&mut self, super_key: &Arc<SuperKey>) {
+        self.key_index.insert(super_key.id, Arc::downgrade(super_key));
+    }
 }
 
 #[derive(Default)]
@@ -79,21 +157,7 @@
 
 impl SuperKeyManager {
     pub fn new() -> Self {
-        Self { data: Mutex::new(Default::default()) }
-    }
-
-    pub fn forget_screen_lock_key_for_user(&self, user: UserId) {
-        let mut data = self.data.lock().unwrap();
-        if let Some(usk) = data.user_keys.get_mut(&user) {
-            usk.screen_lock = None;
-        }
-    }
-
-    pub fn forget_screen_lock_keys(&self) {
-        let mut data = self.data.lock().unwrap();
-        for (_, usk) in data.user_keys.iter_mut() {
-            usk.screen_lock = None;
-        }
+        Default::default()
     }
 
     pub fn forget_all_keys_for_user(&self, user: UserId) {
@@ -101,25 +165,19 @@
         data.user_keys.remove(&user);
     }
 
-    pub fn forget_all_keys(&self) {
+    fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) {
         let mut data = self.data.lock().unwrap();
-        data.user_keys.clear();
-        data.key_index.clear();
-    }
-
-    fn install_per_boot_key_for_user(&self, user: UserId, super_key: SuperKey) {
-        let mut data = self.data.lock().unwrap();
-        data.key_index.insert(super_key.id, Arc::downgrade(&(super_key.key)));
+        data.add_key_to_key_index(&super_key);
         data.user_keys.entry(user).or_default().per_boot = Some(super_key);
     }
 
-    fn get_key(&self, key_id: &i64) -> Option<Arc<ZVec>> {
+    fn get_key(&self, key_id: &i64) -> Option<Arc<SuperKey>> {
         self.data.lock().unwrap().key_index.get(key_id).and_then(|k| k.upgrade())
     }
 
-    pub fn get_per_boot_key_by_user_id(&self, user_id: u32) -> Option<SuperKey> {
+    pub fn get_per_boot_key_by_user_id(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
         let data = self.data.lock().unwrap();
-        data.user_keys.get(&user_id).map(|e| e.per_boot.clone()).flatten()
+        data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
     }
 
     /// This function unlocks the super keys for a given user.
@@ -137,7 +195,7 @@
             .get_or_create_key_with(
                 Domain::APP,
                 user as u64 as i64,
-                KeystoreDB::USER_SUPER_KEY_ALIAS,
+                &USER_SUPER_KEY.alias,
                 crate::database::KEYSTORE_UUID,
                 || {
                     // For backward compatibility we need to check if there is a super key present.
@@ -162,7 +220,8 @@
             )
             .context("In unlock_user_key: Failed to get key id.")?;
 
-        self.populate_cache_from_super_key_blob(user, entry, pw).context("In unlock_user_key.")?;
+        self.populate_cache_from_super_key_blob(user, USER_SUPER_KEY.algorithm, entry, pw)
+            .context("In unlock_user_key.")?;
         Ok(())
     }
 
@@ -173,12 +232,14 @@
     pub fn unwrap_key<'a>(&self, blob: &'a [u8], metadata: &BlobMetaData) -> Result<KeyBlob<'a>> {
         match metadata.encrypted_by() {
             Some(EncryptedBy::KeyId(key_id)) => match self.get_key(key_id) {
-                Some(key) => Ok(KeyBlob::Sensitive(
-                    Self::unwrap_key_with_key(blob, metadata, &key).context("In unwrap_key.")?,
-                    SuperKey { key: key.clone(), id: *key_id },
-                )),
+                Some(super_key) => Ok(KeyBlob::Sensitive {
+                    key: Self::unwrap_key_with_key(blob, metadata, &super_key)
+                        .context("In unwrap_key: unwrap_key_with_key failed")?,
+                    reencrypt_with: super_key.reencrypt_with.as_ref().unwrap_or(&super_key).clone(),
+                    force_reencrypt: super_key.reencrypt_with.is_some(),
+                }),
                 None => Err(Error::Rc(ResponseCode::LOCKED))
-                    .context("In unwrap_key: Key is not usable until the user entered their LSKF."),
+                    .context("In unwrap_key: Required super decryption key is not in memory."),
             },
             _ => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
                 .context("In unwrap_key: Cannot determined wrapping key."),
@@ -186,18 +247,44 @@
     }
 
     /// Unwraps an encrypted key blob given an encryption key.
-    fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &[u8]) -> Result<ZVec> {
-        match (metadata.iv(), metadata.aead_tag()) {
-            (Some(iv), Some(tag)) => aes_gcm_decrypt(blob, iv, tag, key)
-                .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
-            (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
-                concat!(
-                    "In unwrap_key_with_key: Key has incomplete metadata.",
-                    "Present: iv: {}, aead_tag: {}."
-                ),
-                iv.is_some(),
-                tag.is_some(),
-            )),
+    fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &SuperKey) -> Result<ZVec> {
+        match key.algorithm {
+            SuperEncryptionAlgorithm::Aes256Gcm => match (metadata.iv(), metadata.aead_tag()) {
+                (Some(iv), Some(tag)) => key
+                    .aes_gcm_decrypt(blob, iv, tag)
+                    .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
+                (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                    concat!(
+                        "In unwrap_key_with_key: Key has incomplete metadata.",
+                        "Present: iv: {}, aead_tag: {}."
+                    ),
+                    iv.is_some(),
+                    tag.is_some(),
+                )),
+            },
+            SuperEncryptionAlgorithm::EcdhP256 => {
+                match (metadata.public_key(), metadata.salt(), metadata.iv(), metadata.aead_tag()) {
+                    (Some(public_key), Some(salt), Some(iv), Some(aead_tag)) => {
+                        ECDHPrivateKey::from_private_key(&key.key)
+                            .and_then(|k| k.decrypt_message(public_key, salt, iv, blob, aead_tag))
+                            .context(
+                                "In unwrap_key_with_key: Failed to decrypt the key blob with ECDH.",
+                            )
+                    }
+                    (public_key, salt, iv, aead_tag) => {
+                        Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                            concat!(
+                                "In unwrap_key_with_key: Key has incomplete metadata.",
+                                "Present: public_key: {}, salt: {}, iv: {}, aead_tag: {}."
+                            ),
+                            public_key.is_some(),
+                            salt.is_some(),
+                            iv.is_some(),
+                            aead_tag.is_some(),
+                        ))
+                    }
+                }
+            }
         }
     }
 
@@ -205,15 +292,10 @@
     pub fn super_key_exists_in_db_for_user(
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
     ) -> Result<bool> {
         let key_in_db = db
-            .key_exists(
-                Domain::APP,
-                user_id as u64 as i64,
-                KeystoreDB::USER_SUPER_KEY_ALIAS,
-                KeyType::Super,
-            )
+            .key_exists(Domain::APP, user_id as u64 as i64, &USER_SUPER_KEY.alias, KeyType::Super)
             .context("In super_key_exists_in_db_for_user.")?;
 
         if key_in_db {
@@ -232,17 +314,18 @@
         &self,
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         pw: &Password,
     ) -> Result<UserState> {
+        let alias = &USER_SUPER_KEY;
         let result = legacy_migrator
-            .with_try_migrate_super_key(user_id, pw, || db.load_super_key(user_id))
+            .with_try_migrate_super_key(user_id, pw, || db.load_super_key(alias, user_id))
             .context("In check_and_unlock_super_key. Failed to load super key")?;
 
         match result {
             Some((_, entry)) => {
                 let super_key = self
-                    .populate_cache_from_super_key_blob(user_id, entry, pw)
+                    .populate_cache_from_super_key_blob(user_id, alias.algorithm, entry, pw)
                     .context("In check_and_unlock_super_key.")?;
                 Ok(UserState::LskfUnlocked(super_key))
             }
@@ -260,7 +343,7 @@
         &self,
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         pw: Option<&Password>,
     ) -> Result<UserState> {
         let super_key_exists_in_db =
@@ -279,11 +362,22 @@
                 .context("In check_and_initialize_super_key.")?;
 
             let key_entry = db
-                .store_super_key(user_id, &(&encrypted_super_key, &blob_metadata))
+                .store_super_key(
+                    user_id,
+                    &USER_SUPER_KEY,
+                    &encrypted_super_key,
+                    &blob_metadata,
+                    &KeyMetaData::new(),
+                )
                 .context("In check_and_initialize_super_key. Failed to store super key.")?;
 
             let super_key = self
-                .populate_cache_from_super_key_blob(user_id, key_entry, pw)
+                .populate_cache_from_super_key_blob(
+                    user_id,
+                    USER_SUPER_KEY.algorithm,
+                    key_entry,
+                    pw,
+                )
                 .context("In check_and_initialize_super_key.")?;
             Ok(UserState::LskfUnlocked(super_key))
         } else {
@@ -294,19 +388,26 @@
     //helper function to populate super key cache from the super key blob loaded from the database
     fn populate_cache_from_super_key_blob(
         &self,
-        user_id: u32,
+        user_id: UserId,
+        algorithm: SuperEncryptionAlgorithm,
         entry: KeyEntry,
         pw: &Password,
-    ) -> Result<SuperKey> {
-        let super_key = Self::extract_super_key_from_key_entry(entry, pw).context(
-            "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
-        )?;
+    ) -> Result<Arc<SuperKey>> {
+        let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None)
+            .context(
+                "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
+            )?;
         self.install_per_boot_key_for_user(user_id, super_key.clone());
         Ok(super_key)
     }
 
     /// Extracts super key from the entry loaded from the database
-    pub fn extract_super_key_from_key_entry(entry: KeyEntry, pw: &Password) -> Result<SuperKey> {
+    pub fn extract_super_key_from_key_entry(
+        algorithm: SuperEncryptionAlgorithm,
+        entry: KeyEntry,
+        pw: &Password,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
         if let Some((blob, metadata)) = entry.key_blob_info() {
             let key = match (
                 metadata.encrypted_by(),
@@ -315,6 +416,7 @@
                 metadata.aead_tag(),
             ) {
                 (Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
+                    // Note that password encryption is AES no matter the value of algorithm
                     let key = pw.derive_key(Some(salt), AES_256_KEY_LENGTH).context(
                         "In extract_super_key_from_key_entry: Failed to generate key from password.",
                     )?;
@@ -336,7 +438,7 @@
                     ));
                 }
             };
-            Ok(SuperKey { key: Arc::new(key), id: entry.id() })
+            Ok(Arc::new(SuperKey { algorithm, key, id: entry.id(), reencrypt_with }))
         } else {
             Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
                 .context("In extract_super_key_from_key_entry: No key blob info.")
@@ -370,14 +472,14 @@
         &self,
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         key_blob: &[u8],
     ) -> Result<(Vec<u8>, BlobMetaData)> {
         match UserState::get(db, legacy_migrator, self, user_id)
             .context("In super_encrypt. Failed to get user state.")?
         {
             UserState::LskfUnlocked(super_key) => {
-                Self::encrypt_with_super_key(key_blob, &super_key)
+                Self::encrypt_with_aes_super_key(key_blob, &super_key)
                     .context("In super_encrypt_on_key_init. Failed to encrypt the key.")
             }
             UserState::LskfLocked => {
@@ -391,13 +493,17 @@
     //Helper function to encrypt a key with the given super key. Callers should select which super
     //key to be used. This is called when a key is super encrypted at its creation as well as at its
     //upgrade.
-    fn encrypt_with_super_key(
+    fn encrypt_with_aes_super_key(
         key_blob: &[u8],
         super_key: &SuperKey,
     ) -> Result<(Vec<u8>, BlobMetaData)> {
+        if super_key.algorithm != SuperEncryptionAlgorithm::Aes256Gcm {
+            return Err(Error::sys())
+                .context("In encrypt_with_aes_super_key: unexpected algorithm");
+        }
         let mut metadata = BlobMetaData::new();
         let (encrypted_key, iv, tag) = aes_gcm_encrypt(key_blob, &(super_key.key))
-            .context("In encrypt_with_super_key: Failed to encrypt new super key.")?;
+            .context("In encrypt_with_aes_super_key: Failed to encrypt new super key.")?;
         metadata.add(BlobMetaEntry::Iv(iv));
         metadata.add(BlobMetaEntry::AeadTag(tag));
         metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key.id)));
@@ -414,17 +520,52 @@
         domain: &Domain,
         key_parameters: &[KeyParameter],
         flags: Option<i32>,
-        user_id: u32,
+        user_id: UserId,
         key_blob: &[u8],
     ) -> Result<(Vec<u8>, BlobMetaData)> {
-        match (*domain, Enforcements::super_encryption_required(key_parameters, flags)) {
-            (Domain::APP, true) => {
+        match Enforcements::super_encryption_required(domain, key_parameters, flags) {
+            SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
+            SuperEncryptionType::LskfBound => {
                 self.super_encrypt_on_key_init(db, legacy_migrator, user_id, &key_blob).context(
                     "In handle_super_encryption_on_key_init.
                          Failed to super encrypt the key.",
                 )
             }
-            _ => Ok((key_blob.to_vec(), BlobMetaData::new())),
+            SuperEncryptionType::ScreenLockBound => {
+                let mut data = self.data.lock().unwrap();
+                let entry = data.user_keys.entry(user_id).or_default();
+                if let Some(super_key) = entry.screen_lock_bound.as_ref() {
+                    Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
+                        "In handle_super_encryption_on_key_init. ",
+                        "Failed to encrypt the key with screen_lock_bound key."
+                    ))
+                } else {
+                    // Symmetric key is not available, use public key encryption
+                    let loaded =
+                        db.load_super_key(&USER_SCREEN_LOCK_BOUND_ECDH_KEY, user_id).context(
+                            "In handle_super_encryption_on_key_init: load_super_key failed.",
+                        )?;
+                    let (key_id_guard, key_entry) = loaded.ok_or_else(Error::sys).context(
+                        "In handle_super_encryption_on_key_init: User ECDH key missing.",
+                    )?;
+                    let public_key =
+                        key_entry.metadata().sec1_public_key().ok_or_else(Error::sys).context(
+                            "In handle_super_encryption_on_key_init: sec1_public_key missing.",
+                        )?;
+                    let mut metadata = BlobMetaData::new();
+                    let (ephem_key, salt, iv, encrypted_key, aead_tag) =
+                        ECDHPrivateKey::encrypt_message(public_key, key_blob).context(concat!(
+                            "In handle_super_encryption_on_key_init: ",
+                            "ECDHPrivateKey::encrypt_message failed."
+                        ))?;
+                    metadata.add(BlobMetaEntry::PublicKey(ephem_key));
+                    metadata.add(BlobMetaEntry::Salt(salt));
+                    metadata.add(BlobMetaEntry::Iv(iv));
+                    metadata.add(BlobMetaEntry::AeadTag(aead_tag));
+                    metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(key_id_guard.id())));
+                    Ok((encrypted_key, metadata))
+                }
+            }
         }
     }
 
@@ -448,17 +589,15 @@
     /// Check if a given key needs re-super-encryption, from its KeyBlob type.
     /// If so, re-super-encrypt the key and return a new set of metadata,
     /// containing the new super encryption information.
-    pub fn reencrypt_on_upgrade_if_required<'a>(
+    pub fn reencrypt_if_required<'a>(
         key_blob_before_upgrade: &KeyBlob,
         key_after_upgrade: &'a [u8],
     ) -> Result<(KeyBlob<'a>, Option<BlobMetaData>)> {
         match key_blob_before_upgrade {
-            KeyBlob::Sensitive(_, super_key) => {
-                let (key, metadata) = Self::encrypt_with_super_key(key_after_upgrade, super_key)
-                    .context(concat!(
-                        "In reencrypt_on_upgrade_if_required. ",
-                        "Failed to re-super-encrypt key on key upgrade."
-                    ))?;
+            KeyBlob::Sensitive { reencrypt_with: super_key, .. } => {
+                let (key, metadata) =
+                    Self::encrypt_with_aes_super_key(key_after_upgrade, super_key)
+                        .context("In reencrypt_if_required: Failed to re-super-encrypt key.")?;
                 Ok((KeyBlob::NonSensitive(key), Some(metadata)))
             }
             _ => Ok((KeyBlob::Ref(key_after_upgrade), None)),
@@ -472,6 +611,117 @@
         }
         false
     }
+
+    /// Fetch a superencryption key from the database, or create it if it doesn't already exist.
+    /// When this is called, the caller must hold the lock on the SuperKeyManager.
+    /// So it's OK that the check and creation are different DB transactions.
+    fn get_or_create_super_key(
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        key_type: &SuperKeyType,
+        password: &Password,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
+        let loaded_key = db.load_super_key(key_type, user_id)?;
+        if let Some((_, key_entry)) = loaded_key {
+            Ok(Self::extract_super_key_from_key_entry(
+                key_type.algorithm,
+                key_entry,
+                password,
+                reencrypt_with,
+            )?)
+        } else {
+            let (super_key, public_key) = match key_type.algorithm {
+                SuperEncryptionAlgorithm::Aes256Gcm => (
+                    generate_aes256_key()
+                        .context("In get_or_create_super_key: Failed to generate AES 256 key.")?,
+                    None,
+                ),
+                SuperEncryptionAlgorithm::EcdhP256 => {
+                    let key = ECDHPrivateKey::generate()
+                        .context("In get_or_create_super_key: Failed to generate ECDH key")?;
+                    (
+                        key.private_key()
+                            .context("In get_or_create_super_key: private_key failed")?,
+                        Some(
+                            key.public_key()
+                                .context("In get_or_create_super_key: public_key failed")?,
+                        ),
+                    )
+                }
+            };
+            //derive an AES256 key from the password and re-encrypt the super key
+            //before we insert it in the database.
+            let (encrypted_super_key, blob_metadata) =
+                Self::encrypt_with_password(&super_key, password)
+                    .context("In get_or_create_super_key.")?;
+            let mut key_metadata = KeyMetaData::new();
+            if let Some(pk) = public_key {
+                key_metadata.add(KeyMetaEntry::Sec1PublicKey(pk));
+            }
+            let key_entry = db
+                .store_super_key(
+                    user_id,
+                    key_type,
+                    &encrypted_super_key,
+                    &blob_metadata,
+                    &key_metadata,
+                )
+                .context("In get_or_create_super_key. Failed to store super key.")?;
+            Ok(Arc::new(SuperKey {
+                algorithm: key_type.algorithm,
+                key: super_key,
+                id: key_entry.id(),
+                reencrypt_with,
+            }))
+        }
+    }
+
+    /// Decrypt the screen-lock bound keys for this user using the password and store in memory.
+    pub fn unlock_screen_lock_bound_key(
+        &self,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        password: &Password,
+    ) -> Result<()> {
+        let mut data = self.data.lock().unwrap();
+        let entry = data.user_keys.entry(user_id).or_default();
+        let aes = entry
+            .screen_lock_bound
+            .get_or_try_to_insert_with(|| {
+                Self::get_or_create_super_key(
+                    db,
+                    user_id,
+                    &USER_SCREEN_LOCK_BOUND_KEY,
+                    password,
+                    None,
+                )
+            })?
+            .clone();
+        let ecdh = entry
+            .screen_lock_bound_private
+            .get_or_try_to_insert_with(|| {
+                Self::get_or_create_super_key(
+                    db,
+                    user_id,
+                    &USER_SCREEN_LOCK_BOUND_ECDH_KEY,
+                    password,
+                    Some(aes.clone()),
+                )
+            })?
+            .clone();
+        data.add_key_to_key_index(&aes);
+        data.add_key_to_key_index(&ecdh);
+        Ok(())
+    }
+
+    /// Wipe the screen-lock bound keys for this user from memory.
+    pub fn lock_screen_lock_bound_key(&self, user_id: UserId) {
+        let mut data = self.data.lock().unwrap();
+        let mut entry = data.user_keys.entry(user_id).or_default();
+        entry.screen_lock_bound = None;
+        entry.screen_lock_bound_private = None;
+    }
 }
 
 /// This enum represents different states of the user's life cycle in the device.
@@ -479,7 +729,7 @@
 pub enum UserState {
     // The user has registered LSKF and has unlocked the device by entering PIN/Password,
     // and hence the per-boot super key is available in the cache.
-    LskfUnlocked(SuperKey),
+    LskfUnlocked(Arc<SuperKey>),
     // The user has registered LSKF, but has not unlocked the device using password, after reboot.
     // Hence the per-boot super-key(s) is not available in the cache.
     // However, the encrypted super key is available in the database.
@@ -494,7 +744,7 @@
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
         skm: &SuperKeyManager,
-        user_id: u32,
+        user_id: UserId,
     ) -> Result<UserState> {
         match skm.get_per_boot_key_by_user_id(user_id) {
             Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
@@ -517,7 +767,7 @@
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
         skm: &SuperKeyManager,
-        user_id: u32,
+        user_id: UserId,
         password: Option<&Password>,
     ) -> Result<UserState> {
         match skm.get_per_boot_key_by_user_id(user_id) {
@@ -551,7 +801,7 @@
         db: &mut KeystoreDB,
         legacy_migrator: &LegacyMigrator,
         skm: &SuperKeyManager,
-        user_id: u32,
+        user_id: UserId,
         password: &Password,
     ) -> Result<UserState> {
         match skm.get_per_boot_key_by_user_id(user_id) {
@@ -577,18 +827,18 @@
         db: &mut KeystoreDB,
         skm: &SuperKeyManager,
         legacy_migrator: &LegacyMigrator,
-        user_id: u32,
+        user_id: UserId,
         keep_non_super_encrypted_keys: bool,
     ) -> Result<()> {
         // mark keys created on behalf of the user as unreferenced.
         legacy_migrator
             .bulk_delete_user(user_id, keep_non_super_encrypted_keys)
             .context("In reset_user: Trying to delete legacy keys.")?;
-        db.unbind_keys_for_user(user_id as u32, keep_non_super_encrypted_keys)
+        db.unbind_keys_for_user(user_id, keep_non_super_encrypted_keys)
             .context("In reset user. Error in unbinding keys.")?;
 
         //delete super key in cache, if exists
-        skm.forget_all_keys_for_user(user_id as u32);
+        skm.forget_all_keys_for_user(user_id);
         Ok(())
     }
 }
@@ -599,18 +849,38 @@
 /// `Ref` holds a reference to a key blob when it does not need to be modified if its
 /// life time allows it.
 pub enum KeyBlob<'a> {
-    Sensitive(ZVec, SuperKey),
+    Sensitive {
+        key: ZVec,
+        /// If KeyMint reports that the key must be upgraded, we must
+        /// re-encrypt the key before writing to the database; we use
+        /// this key.
+        reencrypt_with: Arc<SuperKey>,
+        /// If this key was decrypted with an ECDH key, we want to
+        /// re-encrypt it on first use whether it was upgraded or not;
+        /// this field indicates that that's necessary.
+        force_reencrypt: bool,
+    },
     NonSensitive(Vec<u8>),
     Ref(&'a [u8]),
 }
 
+impl<'a> KeyBlob<'a> {
+    pub fn force_reencrypt(&self) -> bool {
+        if let KeyBlob::Sensitive { force_reencrypt, .. } = self {
+            *force_reencrypt
+        } else {
+            false
+        }
+    }
+}
+
 /// Deref returns a reference to the key material in any variant.
 impl<'a> Deref for KeyBlob<'a> {
     type Target = [u8];
 
     fn deref(&self) -> &Self::Target {
         match self {
-            Self::Sensitive(key, _) => &key,
+            Self::Sensitive { key, .. } => &key,
             Self::NonSensitive(key) => &key,
             Self::Ref(key) => key,
         }
diff --git a/keystore2/src/try_insert.rs b/keystore2/src/try_insert.rs
new file mode 100644
index 0000000..6dd3962
--- /dev/null
+++ b/keystore2/src/try_insert.rs
@@ -0,0 +1,100 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! The TryInsert trait adds to Option<T> the method
+//! get_or_try_to_insert_with, which is analogous to
+//! get_or_insert_with, but allows the called function to fail and propagates the failure.
+
+/// The TryInsert trait adds to Option<T> the method
+/// get_or_try_to_insert_with, which is analogous to
+/// get_or_insert_with, but allows the called function to fail and propagates the failure.
+pub trait TryInsert {
+    /// Type of the Ok branch of the Result
+    type Item;
+    /// Inserts a value computed from `f` into the option if it is [`None`],
+    /// then returns a mutable reference to the contained value. If `f`
+    /// returns Err, the Option is unchanged.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let mut x = None;
+    /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string()))
+    /// {
+    ///     let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?;
+    ///     assert_eq!(y, &5);
+    ///
+    ///     *y = 7;
+    /// }
+    ///
+    /// assert_eq!(x, Some(7));
+    /// ```
+    fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+        &mut self,
+        f: F,
+    ) -> Result<&mut Self::Item, E>;
+}
+
+impl<T> TryInsert for Option<T> {
+    type Item = T;
+    fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+        &mut self,
+        f: F,
+    ) -> Result<&mut Self::Item, E> {
+        if self.is_none() {
+            *self = Some(f()?);
+        }
+
+        match self {
+            Some(v) => Ok(v),
+            // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
+            // variant in the code above.
+            None => unsafe { std::hint::unreachable_unchecked() },
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    fn fails() -> Result<i32, String> {
+        Err("fail".to_string())
+    }
+
+    fn succeeds() -> Result<i32, String> {
+        Ok(99)
+    }
+
+    #[test]
+    fn test() {
+        let mut x = None;
+        assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string()));
+        assert_eq!(x, None);
+        assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99);
+        assert_eq!(x, Some(99));
+        x = Some(42);
+        assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42);
+        assert_eq!(x, Some(42));
+        assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42);
+        assert_eq!(x, Some(42));
+        *x.get_or_try_to_insert_with(fails).unwrap() = 2;
+        assert_eq!(x, Some(2));
+        *x.get_or_try_to_insert_with(succeeds).unwrap() = 3;
+        assert_eq!(x, Some(3));
+        x = None;
+        *x.get_or_try_to_insert_with(succeeds).unwrap() = 5;
+        assert_eq!(x, Some(5));
+    }
+}