Closed shea256 closed 7 years ago
We already worked out the HD paths here: https://github.com/blockstack/blockstack-portal/issues/131
See this for the keychain model: https://forum.blockstack.org/t/blockstack-keychain-model-lets-discuss/726
Device keychain --> identity branch --> name account --> owner, signing, encryption keychains
m / 888' / 0' / 0' / 0 = name owner
m / 888' / 0' / 0' / 1 = name admin
m / 888' / 0' / 0' / 2 = signature verifier
m / 888' / 0' / 0' / 3 = data encrypter
m / 888' / 0' / 0' / 0' = app keychain
@larrysalibra Does this look good to you?
Keychain model visual:
For bitcoin, we'll simply use the following:
Later, we'll make the wallet multi-address and add the ability to have new paths:
Some quick code I whipped up for this:
import {randomBytes} from 'crypto'
import {HDNode} from 'bitcoinjs-lib'
const masterKeychain = HDNode.fromSeedBuffer(randomBytes(32))
const masterKeychainID = masterKeychain.neutered()
const bitcoinKeychain = masterKeychain.deriveHardened(44).deriveHardened(0)
const firstBitcoinAddress = bitcoinKeychain.deriveHardened(0).derive(0)
const identityKeychain = masterKeychain.deriveHardened(888).deriveHardened(0)
const firstIdentityAddress = identityKeychain.derive(0)
Awesome!
Add another level to identity keychain:
m / 888' / 0' / 0' / 0
m / identity / blockstack on bitcoin / name 0 / name owner
Type | Path |
---|---|
Name 0 - name key | m / 888' / 0' / 0' |
Name 0 - signer key | m / 888' / 0' / 1' |
Name 1 - name key | m / 888' / 1' / 0' |
Name 1 - verifier key | m / 888' / 1' / 1' |
Name 1 - encrypter key | m / 888' / 1' / 2' |
Name 1 - app keychain | m / 888' / 1' / 3' |
Name 1 - app key 0 | m / 888' / 1' / 3' / app-name-hash-31' |
m / blockstack on bitcoin / name 0 / name key
Special case for legacy migration:
Check m / 888' / 0' / 0' / 0
and consider it equivalent to m / 888' / 0' / 0'
.
A couple things we'll need to store externally:
An app key is derived as follows:
m / 888' / 1' / 3' / int-31(app-name-hash)'
Where a int-31 is the integer form of the first 31 bits of a byte sequence.
Also, the hash must include both the name and a salt.
To derive an application-specific key, we should:
m / 888' / 1' / 3'
.S
with sha256(pubkey(m / 888' / 1'))
. Do not disclose S
.app-name-hash
as sha256(app-name || S)
To disclose which apps I use, I upload a signed manifest of all m / 888' / 1' / 3' / int-31(app-name-hash)'
paired with the relevant app-name
.
OK pulling all this together:
Type | Path |
---|---|
Name 0 - name key | m / 888' / 0' / 0' |
Name 0 - signer key | m / 888' / 0' / 1' |
Name 1 - name key | m / 888' / 1' / 0' |
Name 1 - verifier key | m / 888' / 1' / 1' |
Name 1 - encrypter key | m / 888' / 1' / 2' |
Name 1 - app keychain | m / 888' / 1' / 3' |
Name 1 - app key 0 | m / 888' / 1' / 3' / app-name-hash-31' |
Where an ICANN app-name is suffixed with .i
, and a Blockstack app-name is suffixed with .x
. For example, helloblockstack.com
becomes helloblockstack.com.i
, whereas storage-app.id
becomes storage-app.id.x
in the above app-name-hash-31
calculation.
@jcnelson @shea256 If we're going to check two addresses for names for "legacy" names, which means twice the number of api calls to lookup names at an address, two code paths and support this forever, why don't we do this instead?
Type | Path |
---|---|
Name 0 - name key | m / 888' / 0' / 0' / 0 |
Name 0 - signer key | m / 888' / 0' / 1' |
Name 1 - name key | m / 888' / 1' / 0' / 0 |
Name 1 - verifier key | m / 888' / 1' / 1' |
Name 1 - encrypter key | m / 888' / 1' / 2' |
Name 1 - app keychain | m / 888' / 1' / 3' |
Name 1 - app key 0 | m / 888' / 1' / 3' / int-31(app-name-hash)' |
m / blockstack on bitcoin / name 0 / 0' / name key
With this method we don't have any special cases and only need to do 1 address -> names look up per name index.
From slack:
ryanshea [11:01 AM] @larrysalibra @jcnelson wait can one of you remind me why this is a problem again? https://github.com/blockstack/blockstack-browser/issues/330#issuecomment-302905902 GitHub integrate a BIP44-compliant HD keychain system · Issue #330 · blockstack/blockstack-browser blockstack-browser - The Blockstack Browser Portal
[11:01] this model that we put together earlier ^
jude [11:02 AM] if you have unhardened children, then if you lose one key, you lose all of them
ryanshea [11:02 AM] ok yes but I actually don't think this is a problem
jude [11:03 AM] why risk it?
ryanshea [11:03 AM] ok here's why
[11:03]
you can give out m / 888' / 0' / 0'
as your name keychain
[11:03] you just give out a single key
[11:04] and then for that given name, another party knows how to verify your signature and at the same time encrypt something for that name
[11:04] it's a single key tree that needs to be handed out
[11:04] for apps we'll rely on app-specific keychains (edited)
[11:05] but there are cases where fore example with daniel, we just wanted to use the root name key
[11:05] another thing is the name key needs to delegate to child keys in some way
[11:05] so either that key itself is going to be signing delegations
[11:06] or a provably related key is going to need to sign delegations (edited)
jude [11:06 AM]
I thought we agreed that the user is going to have to store a signed {app-name: pubkey}
mapping anyway, so why not simply do so for the name/owner/admin/whatever keys as well?
[11:06] since app public keys are hardened children
[11:06] (and that's unavoidable)
ryanshea [11:07 AM] yeah that's an option
[11:07] I'm actually thinking of a third option right now
[11:07] that's even simpler
[11:07]
m / 888' / 0' / 0' / 0' = app keychain```
[11:08]
where the name owner does key delegation
[11:08]
it's the default "key delegation action" signing key
jude [11:08 AM]
also, recall that the key paths below were agreed to because we don't want to force all the early adopters to re-update their zone files:
```Name 0 - name key m / 888' / 0' / 0' / 0
Name 0 - signer key m / 888' / 0' / 1'
Name 1 - name key m / 888' / 1' / 0' / 0
Name 1 - verifier key m / 888' / 1' / 1'
Name 1 - encrypter key m / 888' / 1' / 2'
Name 1 - app keychain m / 888' / 1' / 3'
Name 1 - app key 0 m / 888' / 1' / 3' / int-31(app-name-hash)'
ryanshea [11:08 AM] what I posed above would accomplish the same thing
[11:09] the last thing larry proposed is unfortunately messy
[11:09] I also realized the name owner key will have to do signing anyway
[11:09] so the signer/verifier key might not even make sense
jude [11:10 AM] um...I thought it was recommended by NIST to have wholly-separate signing/verifying keys from owner ("master") keys?
ryanshea [11:10 AM] yes, key signing keys and payload signing keys (edited)
[11:10] however the key signing key needs to sign the payload signing key
[11:10] so it is doing signing, just at a much lower frequency
[11:11] the reason here is so that the key signing key never has to be rotated (or at least infrequently)
[11:12] so the key signing key can sign a key in the app keychain, that's one option
jude [11:16 AM] let me see if I understand this correctly: Name 0's key paths are:
m / 888' / 0' / 0' / 0' = app keychain root
m / 888' / 0' / 0' / 0' / int-31(hash(foo-app))' = key for app with name "foo-app"
m / 888' / 0' / 0' / 1' = signing/verifying key
m / 888' / 0' / 0' / 2' = encryption key
Name 1's key paths are
m / 888' / 1' / 0' / 0' = app keychain root
m / 888' / 1' / 0' / 0' / int-31(hash(bar-app))' = key for app with name "bar-app"
m / 888' / 1' / 0' / 1' = signing/verifying key
m / 888' / 1' / 0' / 2' = encryption key
(edited)
[11:17] @larrysalibra will have to chime in to clarify whether or not this is compatible with the code that is deployed today
[11:21]
more generically:
Name n
's key paths for version v
of this spec are:
m / 888' / ${n}' / ${v}' / 0' = app keychain root
m / 888' / ${n}' / ${v}' / 0' / int-31(hash(name)) = app-specific key
m / 888' / ${n}' / ${v}' / 1' = signing/verifying key
m / 888' / ${n}' / ${v}' / 2' = encryption key
Also, note that if we go with this scheme, the user still needs to explicitly disclose their public signing and encryption keys, since they are cryptographically unrelated to m
ryanshea [11:22 AM] hmm
[11:23] I'm thinking this over
jude [11:23 AM]
that's unavoidable either way since anything beneath m / 888'
will be underivable from m
without disclosure
[11:24] moreover, the user needs to disclose their app pubkeys either way
[11:25] so, no matter what we do, we're still going to have to store key data in addition to the profile (possibly embedded within the profile)
ryanshea [11:26 AM] yes I was trying to see if we could store a single xpub
[11:26] that was one of the motivations behind this
[11:26]
m / 888' / 0' / 0' / 1 = name admin
m / 888' / 0' / 0' / 2 = signature verifier
m / 888' / 0' / 0' / 3 = data encrypter
m / 888' / 0' / 0' / 0' = app keychain
jude [11:27 AM] the whole point of having hardened children is to make it so you can't derive public keys from your master public key
ryanshea [11:27 AM] yes but perhaps you want to allow that
[11:27] in this case we want someone to derive keys / 0 through / 3
[11:27]
and the important thing is that m / 888' / 0' / 0'
is what is to be protected (edited)
[11:28] think of them as role variants of the same key
jude [11:28 AM] except they really are the same key
[11:28] if I have one unhardened child, I have all of them
[11:28] the search space of 2^31 won't stop me from discovering literally all of your derived keys
ryanshea [11:29 AM] there are only 4 derived keys here
[11:29] all for of which I want you to know
[11:29]
as in, I give you this
m / 888' / 0' / 0'
[11:29] and then I'm like "verify my message"
[11:29]
and you derive
m / 888' / 0' / 0' / 2
larry [11:30 AM] catching up
jude [11:31 AM] yes, I remember having this conversation. The point I made then was that there are some unknown-unknowns here, such as the possible existence of a related-key-like attack whereby I might be able to crack your private key using signatures from your derived children
ryanshea [11:31 AM] yes right ok
jude [11:31 AM] the existence of the relationship between your unhardened children may lead to future cryptographic breaks
[11:31] but we also all agreed that none of us have the mathematics background to go reason about this effectively--it's entirely possible that there is no risk (but I can't prove it either way) (edited)
[11:33] I also want to remind you that since we've already agreed that app-specific keys are hardened children, we already find ourselves needing to store key data alongside the profile no matter how we derive signing/encryption/owner keys
[11:33] so, there's no point to making owner/encryption/signing keys unhardened--there's no upside to be had, but there is a potential downside (edited)
larry [11:33 AM] if they really are the same key because they're all derivable, then we should explicitly use the same key https://blockstack.slack.com/archives/C074LC7RC/p1496330909678352 jude except they really are the same key Posted in #engineeringToday at 11:28 AM
[11:37] another option is to move the non-name owner keys to arbitrary locations and expose both the derivation path and the public key in the zonefile or profile.
[11:37] this would allow users to rotate keys
[11:37] and they could use the derivation path to derive the private key (edited)
[11:38] otherwise, if i transfer my name to a new address, all of the other keys also become invalid...i have to re-encrypt all of my data as part of the transfer...right? (edited)
aaronb [11:40 AM] Wait-- I think this needs clarification-- derivation of child keys is done with the parent pubkey, a (random) chain code, and an index as input, meaning that if there was a "related key attack" that attack would work for the public key on its own jude the existence of the relationship between your unhardened children may lead to future cryptographic breaks Posted in #engineeringToday at 11:31 AM
jude [11:41 AM]
Wait-- I think this needs clarification-- derivation of child keys is done with the parent pubkey, a (random) chain code, and an index as input, meaning that if there was a "related key attack" that attack would work for the public key on its own the chaincode would be publicly known
aaronb [11:41 AM] but that doesn't matter if you're only worried about exposing the parent private key
larry [11:41 AM] didn't we find out that the chain code is always "bitcoin seed" in bitcoinjs-lib?
jude [11:41 AM] yup
[11:42]
but that doesn't matter if you're only worried about exposing the parent private key what I'm worried about is that an attacker might be able to use the signatures from the set of unhardened children to break the parent private key. The attacker would have more signatures (and nonces) to work with than they would have if the keys were truly separate (edited)
aaronb [11:43 AM] ah, I see
larry [11:43 AM] that makes sense
jude [11:43 AM] I'm not sure how (or if) the attack would work, however. This is why I call it an unknown-unknown
larry [11:44 AM] i can imagine situations where you want one of your keys to change (such as the name owner key) but don't want others to change (such as the encryptor key)
[11:45] in that case it seems like we shouldn't use fixed paths to derive these other keys because we wouldn't be able to change 1 without changing all of them
ryanshea [11:46 AM] why would you rotate your name owner key?
larry [11:47 AM] because i restored my wallet in a public airport and i dont trust the confidentiality of the mnemonic anymore, so want to move my names to another wallet. i want to move one name to another wallet so that I can use it separately from some others.
[11:47] maybe one name is work related
jude [11:49 AM] the name owner key is in the blockain, but all other keys can be put into the zonefile. So, we could simply allow the user to pick the derivation path just as long as they don't reuse paths
ryanshea [11:49 AM] yeah but then you'd just get a new mnemonic
[11:49] and start the whole keychain over
[11:50] and use a migration tool built in to the browser
jude [11:50 AM]
then how about this? put the signer and encryption public keys in the zone file and we use a random derivation path from m
each time the user wants to update any of them? (edited)
[11:51]
requires a NAME_UPDATE
though
ryanshea [11:51 AM] that's not great for importing
[11:51] then you need to store the random state
[11:51] the entire reason for deterministic paths is so that you can switch devices with just the 12 word phrase
larry [11:51 AM] yes
ryanshea [11:51 AM] paths can be scanned
[11:51] state can be restored
jude [11:52 AM]
hold on--I thought m
was going to be specific to the device?
[11:52]
so, when does a user ever import m
?
ryanshea [11:52 AM] in the long term it will be
[11:53] but even then, let's say you want to switch browsers
[11:53] or let's say your state got lost and you want to restore it
[11:53] on the same device
larry [11:53 AM] where will the relationships between devices be stored?
[11:53] zone file?
jude [11:53 AM] that's what I was planning on
ryanshea [11:53 AM] yeah but the zone file only applies to a single name
[11:54] so you can't rely on that
[11:54] let's say I have 3 devices and 10 names
[11:54] unless you do it on a per name basis
[11:54] copay lets you have different arrangements
jude [11:55 AM] was thinking that was going to be the case
[11:55] my "work" name identifies my "work" devices (edited)
ryanshea [11:55 AM] yeah that could work
[11:55] will just be a challenge to get it as simple as possible
jude [11:56 AM] no matter what we do, we're heading to a world where names are controllable only from certain devices anyway
[11:56] Gaia needs to know which devices can write to datastores under a specific name, so we're going to need to let it know which devices are linked to a name already
larry [11:58 AM] it sounds like we need to figure out the multi device story sooner rather than later
[11:58] from the user's perspective, i expect to be able to use my name from all of my devices. i expect that if i lose my phone, i can still use my name from my laptopp
[11:59] and that there's some way to revoke my phone
ryanshea [11:59 AM] yes exactly
jude [11:59 AM]
so, each device gets a separate m
ryanshea [11:59 AM] short term I think the solution is load your phrase onto multiple computers and then add in hardware keys
[11:59] simple enough model
jude [11:59 AM]
and maybe something like b58check_encode(ripemd160(double_sha256(pubkey(m / 888'))))
is the device ID (edited)
larry [11:59 AM] i expect that given a backup phrase, i can restore one device and then also use that device to re-provision other devices
ryanshea [11:59 AM] long term is each device gets a keychain
[11:59] and you pair devices
[12:00] pairing for each name would be nice, but I see that as an advanced feature
[12:00] way too complex to ask a user to do that
larry [12:00 PM] how do the hardware keys interact with our current keychain?
jude [12:03 PM]
maybe the user could have a master key that derives each m
as a hardened child?
[12:04]
more generically, derive m
for a given device using a "master" mnemonic. Doesn't have to be bip32 per se
[12:04]
maybe it could be m = master / int-31(hash(user's chosen device name))'
ryanshea [12:05 PM] with multi-device pairing I'd actually generate completely separate keys, and if you can't do that because of short term implementation complexity, I'd just focus on a single keychain copied across all devices
[12:05] we should look into how complex it would be to implement device pairing
[12:05] and make an assessment on how soon we should tackle that
larry [12:06 PM] have either of you looked at how keybase does device pairing?
ryanshea [12:06 PM] yeah it's nice
jude [12:08 PM] see, this is why I thought using the zone file to identify devices was a good idea
[12:08] as long as you control the name owner key for that name, you can add and revoke devices at will
[12:09] the name owner key itself would have to have a mnemonic representation, so you could recover it on any device (possibly a brand-new one) and use it to re-key
[12:12]
and we already do this, since we back up your encrypted m
when you log into Portal for the first time (edited)
[12:14] related, I think it's generally a good idea to keep the derivation specifics isolated within Portal, and isolated to the key owner. Other users and other subsystems should not care how you derive your public keys; they should only care about being able to get them
[12:16] Gaia shouldn't have to know or care about the derivation steps you used; Gaia only has to find your app-specific public keys (which can be delivered in a signed JSON blob) and find your signing key (which should be listed in your zonefile anyway)
aikon3390 [12:19 PM] joined #engineering
jude [12:22 PM] so, how about this:
m
and backs it up (encrypted); allows you to re-key (edited)fletcher [12:26 PM] joined #engineering
muneeb [12:26 PM]
I’d just focus on a single keychain copied across all devices
[12:27] can be a security issue even in the short run
[12:27] lose 1 device and everything can be compromised
jude [12:27 PM]
moreover, in something like Qubes OS, you'd have a m
for each container
Background on the weaknesses of unhardened (extended) keys: https://bitcoin.org/en/developer-guide#hardened-keys
Good point @jcnelson.
Thinking about multi-device derivation now.
Can we use a "privacy derivation seed" for allowing multiple devices to derive each other's children?
This is comparable to the privacy key vs spending key model in the stealth address system.
This is the keychain derivation scheme we came up with:
It is backwards compatible with the current (0.9.0 & higher) browser wallet format.
Adding a device requires the secrets in red which are generated from the master secret m. After adding devices, users will be prompted to "lockdown" their device which removes the red secrets and leaves only the device specific secret k
There exists a key delegation file which contains a signed bundle of public keys so that people can verify that keys belong to the right person.
@jcnelson can you add documentation on the key delegation file? tagging @shea256
Gaia is dependent on this.
I don't think we should do it this way @larrysalibra @jcnelson. Each device should have it's own independent m.
Also keychain secrets shouldn't be shared across devices.
Also if you register a new name, you shouldn't have to repair the other device.
From my understanding, I think the delegation file is an important part of the design that solves the problem you're raising, @shea256. The delegation file is signed by the owner key, which then lists keys like the device-specific identity public key.
To register a name, the client that registers the name is able to construct m/888'/2'/2
, which allows it sign a new delegation file that delegates to the existing device keys. So when a registration is successful, you won't have to repair the other devices.
This design would depend on the delegation system, though.
There needs to be a standard way for discovering public keys. In particular, we need to:
discover the set of signing public keys for the user (there will be one per device), so we can identify profiles and other messages signed by them.
discover the set of app-specific public keys that will be used to authenticate data from Gaia datastores.
This will entail creating a "key delegate" JWT that lists the set of signing keys for a user. The key m/888'/${n}'/${n}
is the "owner" key for the nth
name, and the key m/889'/${d}'/${n}'/${n}'
is the "signing" key for the nth
name on the dth
device. The key delegate JWT is the set of keys m/889'/${d}'/${n}/${n}
for all d
, signed by m/888'/${n}'/${n}
.
The key delegate JWT will be stored alongside the profile. The profile is signed with either the identity key m/888'/${n}'/${n}
, or any of the keys in the delegate JWT. In the latter case, the profile validation protocol is to load the profile (but without trusting it), load the delegate JWT it points to, and then verify that (1) the delegate file is signed by m/888'/${n}'/${n}
, and (2) at least one of the keys in the delegate file signed the profile.
The point of the above is to ensure that you can update your profile from any device without needing m
or your main identity key m/888'/${n}'/${n}
. Instead, you'd use your device-specific identity key, which anyone can verify corresponds to your main identity key by looking up and verifying your delegate file. The only time you need m
is to add or remove device-specific keys from the delegate file.
Also keychain secrets shouldn't be shared across devices.
When you add a new device, you only need to enter the device specific k from the device that has m. That device at the same time updates the delegation file with the public key of the new device's k.
If none of your devices have m (and the ability to derive the keys in red in the diagram), you would have to restore m to that device by entering the 12 word mnemonic on that device. Doing so would make it so that device can issue name related transactions and add or remove devices.
Figure of keychains across multiple devices:
https://raw.githubusercontent.com/blockstack/blockstack-browser/kantai-patch-1/docs/keychain.png
The red nodes in that figure participate in a "2 of 3" multisig for name operations and signing of the key delegate object (which delegates power to the device keys).
The blue nodes in that figure are the children keys used for signing, encryption, and application data.
Interesting discovery: JWTs can have multiple signatures.
From the spec (http://self-issued.info/docs/draft-jones-json-web-token-01.html):
- JWT Serialization Formats JSON Web Tokens (JWTs) support two serialization formats: the JWT Compact Serialization, which is more space efficient and intended for uses where the token is passed as a simple string-valued parameter, and the JWT JSON Serialization, which is more general, being able to contain multiple signatures over the same content. The two serialization formats are intended for use in different contexts.
Here's an example they include:
{"header":[
"eyJhbGciOiJSUzI1NiJ9",
"eyJhbGciOiJFUzI1NiJ9"],
"payload":"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
"signature":[
"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw",
"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"]
}
Thinking about a model like this. Here, the name owner key is used to sign delegations to the device keychains.
I just ran an experiment and updated jsontokens-js
so that it supports an expanded, unencoded form of JSON Web Tokens. This is making me pretty excited.
{
"header":["eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ"],
"payload":"{\"issuedAt\":\"1440713414.85\",\"challenge\":\"7cd9ed5e-bb0e-49ea-a323-f28bde3a0549\",\"issuer\":{\"publicKey\":\"03fdd57adec3d438ea237fe46b33ee1e016eda6b585c3e27ea66686c2ea5358479\",\"chainPath\":\"bd62885ec3f0e3838043115f4ce25eedd22cc86711803fb0c19601eeef185e39\",\"publicKeychain\":\"xpub661MyMwAqRbcFQVrQr4Q4kPjaP4JjWaf39fBVKjPdK6oGBayE46GAmKzo5UDPQdLSM9DufZiP8eauy56XNuHicBySvZp7J5wsyQVpi2axzZ\",\"blockchainid\":\"ryan\"}}",
"signature":["DUf6Rnw6FBKv4Q3y95RX7rR6HG_L1Va96ThcIYTycOf1j_bf9WleLsOyiZ-35Qfw7FgDnW7Utvz4sNjdWOSnhQ"]
}
sure, wrapping a JWT within a JWT would work as a backwards-compatible solution :) Generalizes to n signatures as well.
I would also propose that we always sign in a stable order, i.e. from low-value public key x-coordinates to high-value public key x-coordinates (since in compressed keys, this is the only numeric information that is retained).
@jcnelson Yes but to clarify, that's not a JWT inside a JWT. It's a single JWT in expanded form, but with a slight modification of keeping the payload unencoded. The JWT expanded form is expressed as an object/dictionary instead of as a string with periods in between the parts. The header and payload are each arrays so that multiple signatures can be included.
And makes sense RE signing. We can take a look at the jsontokens-js
code and see how that is currently being done.
@jcnelson @larrysalibra Question RE the keys.
In what contexts will the user use the top level signature signing and decryption keys for a given name. That is, the list signature verifying and encryption keys that aren't associated with a particular app.
Of the top of my head, I'm thinking that authentication should be done with either (a) the same key that owns the name or (b) a key that the name delegates to, which could be the top level signing key.
Pasting in image from https://raw.githubusercontent.com/blockstack/blockstack-browser/kantai-patch-1/docs/keychain.png for visual reference:
In what contexts will the user use the top level signature signing and decryption keys for a given name. That is, the list signature verifying and encryption keys that aren't associated with a particular app.
If I want to send data encrypted for a certain user outside of the context of an app, I would encrypt it with the top level public encryption key. Same with signatures. For example, I want to send ryan.id
an encrypted file. I don't know/care which app he uses to view it, in this case, I'd use the top-level encryption keys.
Of the top of my head, I'm thinking that authentication should be done with either (a) the same key that owns the name or (b) a key that the name delegates to, which could be the top level signing key.
In our discussions, we assumed that the top-level signing key would be used for authentication.
Alternatively, we could forgo any dedicated keys beneath an owner key, and say that everything is an app. If I want to send you an encrypted message, I'm going to be using an app to do so. That app can be built into Blockstack, but it's still an app and thus should still use an app-specific key.
App keys will generally be generated from domain names, icann or blockstack. It seems strange to me to diverge from this convention for encryption and signing keys by using arbitrary strings (that aren't registered domain names) to generate those keys.
Catching up late on this. One quick comment:
[11:42]
but that doesn't matter if you're only worried about exposing the parent private key
what I'm worried about is that an attacker might be able to use the signatures from the set of unhardened children to break the parent private key. The attacker would have more signatures (and nonces) to work with than they would have if the keys were truly separate (edited)
aaronb [11:43 AM]
ah, I see
larry [11:43 AM]
that makes sense
jude [11:43 AM]
I'm not sure how (or if) the attack would work, however. This is why I call it an unknown-unknown
For me this is more than an unknown-unknown, I'd assume that whenever we leak additional information the attack vector increases (now the attacker has more information than the other option and can potentially use that information). Using that rule of thumb, we should avoid this.
@muneeb-ali in general we should minimize the amount of information we disclose. (added to requirements)
@larrysalibra where is the requirements document?
@shea256 it's just a list of bullet points at the top of this issue. no formal doc at the moment.
feel free to add.
Alternatively, we could forgo any dedicated keys beneath an owner key, and say that everything is an app. If I want to send you an encrypted message, I'm going to be using an app to do so. That app can be built into Blockstack, but it's still an app and thus should still use an app-specific key.
Yes exactly. This was my thinking with the diagram above. There would be the branch m/888'/0'/0' and then there's the name owner key at the "0" leaf and then every app is a hardened leaf off of that. This would include the browser portal itself. The browser portal would be considered just another app.
App keys will generally be generated from domain names, icann or blockstack. It seems strange to me to diverge from this convention for encryption and signing keys by using arbitrary strings (that aren't registered domain names) to generate those keys.
Building off of this, we could have the browser portal's "domain" or "origin" be simply "localhost". The main purpose of our app-specific keys is to separate keys by origin. Local storage is organized by origin so this is a nice corollary.
Along the lines of keys being used for a high volume of signatures, we'll need to be able to rotate the app-specific keys as well. This could be done simply by rotating the salt.
Building off of this, we could have the browser portal's "domain" or "origin" be simply "localhost". The main purpose of our app-specific keys is to separate keys by origin. Local storage is organized by origin so this is a nice corollary.
Not sure this is such a good idea in that any app running on localhost would share the same keys as the portal. Is that something we want?
Along the lines of keys being used for a high volume of signatures, we'll need to be able to rotate the app-specific keys as well. This could be done simply by rotating the salt.
Where does the salt come from?
Not sure this is such a good idea in that any app running on localhost would share the same keys as the portal. Is that something we want?
Sorry, to clarify, it would be "localhost:8888" instead of just localhost. Only one app can run on "localhost:8888" at a time and all such apps running on "localhost:8888" will share local storage anyway.
Along the lines of keys being used for a high volume of signatures, we'll need to be able to rotate the app-specific keys as well. This could be done simply by rotating the salt.
Jude and I figured out earlier we'd need a salt when we were exploring the int32(hash(appname)) stuff.
I think for right now, it's important to spec out the paths that will ultimately require "update" transactions. App keys won't, so maybe we can fully decide on the app key paths at another time, and just go with something that works for them for now?
Agree @kantai
We need to encrypt the app key information to prevent others from knowing which apps you are using. To do this, we need the encrypt key to live outside of the app key hierarchy.
We plan to use a symmetric key to encrypt the app names, store the cipher text in the token file. Sharing that you are using an app with someone will involve sharing the symmetric key.
I've merged the new keychain format into v0.10
. Registrations from this commit on won't need to be migrated. https://github.com/blockstack/blockstack-browser/commit/920e406fc5f753ffc491cc3670410587f2f23598
Don't use hashcode #538
We'll also need to create a new issue to update token file to create the key delegation bundle before we can close this out.
Requirements: