Store / access private data linked to specific user

During my exloration of the API I came to the point where I wanted to store / access private data which is linked to a specific (authenticated) user and should only be able to be accessed by this user. There is the concept of PrivateMutableData. In order to work with this you need a secret key. As mentioned in other threads, such key of course could be stored in the browser session etc. but this isn’t a permanent storage. In non-SAFE apps I would typically use some kind of UserID (or some other string retrieved from this ID) as a secret key and after authentication I get access to my UserID / key. How can this be achieved on the SAFEnet?

I would expect some method like (pseudo code):
authenticator.getPrivateUserKey() or something which returns a handle which identifies the currently logged in user. How should this be solved, with the existing API methods?

Forgive me if I’m missing something, but you have access to the user’s encrypted storage (protected by their credentials).

This includes the ‘own_container’ exclusive to each app, so maybe you can store anything that needs to be secure in that?

Thanks for chiming in @happybeing !

Hm, so you would suggest using the own_container of the APP and store PrivateMDs there? Maybe that would be possible but you would still need to iterate all available items then, right?

I’m thinking about creating a base data structure which is directly linked / adressable through a user specific handle / identifier. Maybe a equivalent to the APPs own_container just for the user. Therefore my idea was to create a named PrivateMD where the name would be some kind of user ID and the enc key could be derived from this ID. The way I see it it’s currently not possible to get such ID from the authenticator / API. But is there some other way to achieve what I want?

I hope this makes it somehow clearer what I want to achieve and why. If it doesn’t make sense or you have a better idea just let me know.

EDIT: Another use-case that comes to my mind is a public chat where (for whatever reason) you would like to directly link each message to their respective user and when I come back at a later point I automatically get identified as this specific user without storing this information in the session or data storage etc.

After I replied I began to wonder if it wasn’t a solution after all, because I’m not sure if you can just store data in own_container as private, or if you would still need to encrypt it and store a separate key. So that’s a question for @hunterlester or @bochaco from me! :slight_smile:

Again, speaking from little understanding… would you really need to iterate? Can’t you know what key you are storing under (as in key /value, not encryption key) and just access your data ‘value’ (MD you created) using they key?

Again I’m far from sure any of what I’m saying makes sense so treat with caution! :slight_smile:

That’s exactly the pain point. I can’t. The key should be user specific, right? But I currently don’t know anything about the logged-in user. The credentials etc. are passed to the authenticator but I’m not able to retrieve a user specific identifier through the API.

I could ofc store some chosen ID in the browser session or local storage, but that’s not permanent and not really dependant on the currently authenticated user.

Btw: with “iterating items” I actually meant iterating the MDs itselve.

I’ve probably misunderstood here and haven’t had time to read your descriptions thoroughly today, but that won’t stop me commenting :wink: … I’m still wondering about the above.

Do you need it to be user specific when the storage is 1) only accessible to that user, and 2) only accessible to your app when that user is logged in.

I probably missed the reason for user specific though, so sorry if I’m talking rubbish here.

Wouldn’t that be the user public key. Then only the user can decode it with their private key.

Or have I mis-skimmed the discussion

If you serialise the Private MD it will contain also the encryption keys, so you can just store the serialised version of the Private MD in the app’s own container, which is also a Private MD.
Note that you need to explicitly encrypt the entries before inserting them in a Private MD or Container using the encryptKey and encryptValue functions of the MD API.

1 Like

I think you are right. It wouldn’t be necessary. My thought however is, that it would be practical to just being able to retrieve a unique user identifier and use it for app’s auth logic. But your inputs were helpful in a way that I now think it can also be achieved by generating some ID at the beginning and store this info in a PrivateMD. I have to play around more with PrivateMDs to be sure if this is correct though. :slight_smile:

Also thanks @bochaco for your explanation!

Does someone have a simple code snippet of storing / accessing a (maybe named) PrivateMD (every step necessary including values)? I found these isolated “official” snippets released by Maidsafe very helpful but they were just about PublicMDs.

2 Likes

Okay, it seems Private MD are getting not enough love. :frowning: :smiley: I can’t even create a nonce for a Private MD. Anyone experiencing the same issue? Just raised a github issue.

I’ve been meaning to reply to this thread for a while.

I’m experimenting in the web api playground.

I obtained the public signing key, got the raw 32 byte key, then used that to create a new private MD.
I then encrypted an entry and inserted.
Freed everything from memory, refreshed, obtained public signing key, got raw 32 byte key, generated new nonce, and used the raw key as secret key once more.
I was able to re-obtain the private MD, then decrypt my entry.

Although the public signing key is probably not meant for this purpose, it does work.

I’ll need someone else to chime in about what might be problematic with this.

Back to your original point though, you are right, there is no function specifically for obtaining a secret key associated with your authenticator account.

I’m glad you bring this up though because I have more questions myself

Should there be a signing key that is associated with one’s authenticator account or should it be tied to the application same as the public signing key?

2 Likes

An authenticator signing key could be useful for websites that want to work with each other and exchange data that a user has (username) but one tied with the account and the app may be better for security as could stop bad sites from getting access to its data and do stuff like sell it on, although a combination of the two might make sense i.e username use authenticator signing key but date of birth and real name use account app signing key.

Edit: Upon rereading the safe docs I realised I haven’t figured out what a lot of the handles mean, what is the difference between:
App Public Sign Key,
App Public Encryption Key,
Sign Key,
Key Pair,
Public Encryption Key,
Secret Encryption Key?
also there seems to be a typo in the docs with window.safeCrypto.secEncKeyKeyFromRaw and window.safeCrypto.pubEncKeyKeyFromRaw or is that actually the key’s key handle?

I suggest you read the doc of the Sodium crypto library, especially the different possibilities of Public-key cryptography. Reading this doc can understand the different cryptographic functions.

https://download.libsodium.org/doc/

3 Likes

This sounds very interesting! I know it’s much to ask for but could you be so kind and provide a code snippet which covers these steps? This would help me (and probably others too) a lot!

As I mentioned above I really think there are use-cases where such user identifying (unique) key or handle could make sense. In the end it’s up to the app developer if he wants to take advantage of this and provide a service where the identity of the user plays a role and of course its up to the user if he or she wants to use such service (or prefer to stay anonymous), but at least being able to tie information to specific users would definitely be helpful. And yeah, in these cases such a uniqe user ID or key would be really handy I think.

1 Like

Wow, I was trying to make a simple code snippet and practice cleaning up my promises. :grinning: That didn’t work out but, anyway, this is what I did to create a private MD for encrypting entries, relying on public signing key:

let appInfo = {
  id: 'net.maidsafe.api_playground.webclient.10',
  name: 'SAFE web API playground',
  vendor: 'MaidSafe Ltd.',
  scope: null
};

let encryptedEntries = {};

function decryptQueue(i) {
  let entries = Object.keys(encryptedEntries);

  if ( i === entries.length ) {
    console.log('Decrypted entry: ', encryptedEntries);
    return;
  }

  window.safeMutableData.decrypt(mdHandle, encryptedEntries[entries[i]].buf).then(decryptedValue => {
    encryptedEntries[entries[i]] = String.fromCharCode.apply(null, new Uint8Array(decryptedValue));
    decryptQueue(i + 1);
  })
}

window.safeApp.initialise(appInfo).then(appHandle => window.safeApp.authorise(appHandle, {
        _public: ['Read', 'Insert', 'Update', 'Delete'],
        _publicNames: ['Read', 'Insert', 'Update', 'Delete']
      }, { own_container: true })
      .then(authUri => window.safeApp.connectAuthorised(appHandle, authUri)))
      .then(appHandle => window.safeCrypto.sha3Hash(appHandle, 'explicitly_named_md')
        .then(hashedString => ({hashedString, appHandle})))
      .then(({hashedString, appHandle}) => window.safeCrypto.getAppPubSignKey(appHandle)
        .then(signKeyHandle => window.safeCryptoSignKey.getRaw(signKeyHandle).then(rawSignKey => ({rawSignKey, hashedString, appHandle}))))
      .then(({rawSignKey, hashedString, appHandle}) => window.safeCrypto.generateNonce(appHandle).then(nonce => ({rawSignKey, hashedString, appHandle, nonce})))
      .then(({rawSignKey, hashedString, appHandle, nonce}) => window.safeMutableData.newPrivate(appHandle, hashedString, 15001, rawSignKey.buffer, nonce.buffer)
        .then(mdHandle => window.safeMutableData.encryptKey(mdHandle, 'key1').then(encryptedKey => ({encryptedKey}))
          .then(({encryptedKey}) => window.safeMutableData.encryptValue(mdHandle, 'encrypted value').then(encryptedValue => ({encryptedKey, encryptedValue}))
            .then(({encryptedKey, encryptedValue}) => window.safeMutableData.quickSetup(mdHandle)
              .then(() => window.safeMutableData.newMutation(appHandle)
                .then(mutationHandle => window.safeMutableDataMutation.insert(mutationHandle, encryptedKey, encryptedValue)
                  .then(() => window.safeMutableData.applyEntriesMutation(mdHandle, mutationHandle).then(() => ({mdHandle, appHandle, hashedString})))
                )
              )
            )
          )
        )
      )
      .then(({mdHandle, appHandle, hashedString}) => window.safeMutableData.getEntries(mdHandle)
        .then(entriesHandle => window.safeMutableDataEntries.forEach(entriesHandle, (k, v) => {
        let key = String.fromCharCode.apply(null, k);
        let value = String.fromCharCode.apply(null, new Uint8Array(v.buf));

        console.log('Key: ', key, ',', 'Value: ', value);
      }).then(() => ({appHandle, hashedString}))
    ))
    .then(({appHandle, hashedString}) => window.safeCrypto.getAppPubSignKey(appHandle)
      .then(signKeyHandle => window.safeCryptoSignKey.getRaw(signKeyHandle).then(rawSignKey => ({rawSignKey, hashedString, appHandle})))
      .then(({rawSignKey, hashedString, appHandle}) => window.safeCrypto.generateNonce(appHandle).then(nonce => ({rawSignKey, hashedString, appHandle, nonce})))
      .then(({rawSignKey, hashedString, appHandle, nonce}) => window.safeMutableData.newPrivate(appHandle, hashedString, 15001, rawSignKey.buffer, nonce.buffer))
      .then(mdHandle => window.safeMutableData.getEntries(mdHandle)
        .then(entriesHandle => window.safeMutableDataEntries.forEach(entriesHandle, (k, v) => {
          Object.defineProperty(encryptedEntries, k, {value: v, writable: true, enumerable: true, configurable: true});
        }))
      )
    )
    .then(() => {
      decryptQueue(0);
    })

UPDATE: see my latest post for updated code

5 Likes

Indeed, wow! Many thanks @hunterlester for putting this together! Took me a little while to “decypher” this code :wink: Am also still not that familiar with these promises. However this helped me a lot to get a better grasp of how private MDs work!

One last, maybe dumb, question: Would this value / MD which we stored with the code above be only accessible / decryptable by the user who stored it? Because it’s a Private MD I would expect this, but am not sure. I also couldn’t verify it because I have only one account.

2 Likes

@hunterlester, It seems to me you won’t be able to decrypt what you are storing there.
I think you should generate an encryption key pair with generateEncKeyPair and use the secret encryption key instead of using the app’s public sign key, i.e. changing window.safeCrypto.getAppPubSignKey for window.safeCrypto.generateEncKeyPair and use window.safeCryptoKeyPair.getSecEncKey(encKeyPairHandle).

1 Like

I just tested it and was able to decrypt the value. I think the point was to have a static key instead of a random one, which can’t be stored permanently in a web app.

2 Likes

Ohh ok, so I guess that’s because we are then using symmetric encryption in the Private MutableData. I was wrong then somehow assuming there was an asymmetric encryption being used, cool, thanks @Mindphreaker for testing and clarifying.

2 Likes

You bring up a good question though. So far I have found that the private MD can only be decrypted by the application that created and encrypted it’s entries, since the public signing key is tied to the application. Easy to verify by changing a little information in the appInfo object and trying to decrypt an entry.

What I want to confirm, by creating another account, is if an application public signing key is also tied to the user’s authenticator account? If not, wouldn’t I simply be able to create an application with the same appInfo as you, for example, in order to obtain an identical signing key for decryption, therefore compromising privacy?

Are you on test network 18?

I want to perform a test. I’ll create an explicitly named private MD using public signing key. I’ll then pass you the exact appInfo object that i used to create application, as well as the name of the private MD for you to hash, so that you can verify if it generates an identical public signing key that you can use to decrypt my entry.

If it is the case, how likely would it be for a hacker to be able to obtain the name of the private MD in addition to the appInfo used to generate the public signing key? I don’t know how to quantify that.

Also, is this a case where we need to be asking for a secret key that is unique solely to our user authenticator account?

cc: @bochaco

CORRECTION:
Thanks to @bochaco for bringing up that we should be using getAppPubEncKey instead of getAppPubSignKey. I overlooked it because I was previously experimenting with generating key pairs, which are unique each time generated and meant for asymmetric encryption, not symmetric as we are intending.
The question still stands though and I want to see if I can replicate getAppPubEncKey with another user account.

4 Likes