Nitrokey / nitrokey-websmartcard

Nitrokey WebSmartCard Specification and Documentation
28 stars 3 forks source link

API signature for resident and derived keys #17

Open jans23 opened 4 years ago

jans23 commented 4 years ago

A) Generic API signature for both resident and derived keys

Device API

Sign(to_be_signed_data_hash, public_key, hash, key_handle, origin) Decrypt(to_be_decrypted_data, public_key, hash, key_handle, origin)

  1. Check if public key matches any stored (resident) key and origin. If not, continue with step 2, otherwise step 3.
  2. Derive key: private_key = KDF(master key, key_handle, origin) and verify it's validity against hash.
  3. Compute key operation with payload.
  4. Return result.

Advantages

Disadvantage

B) Different API signatures for resident and derived keys

key_handle: e.g. 256 bit random value

JavaScript API

sign_wrapper(to_be_signed_data_hash, public_key, [hey_handle], [key_id]):
​    IF key_handle THEN:
​    ​    sign(to_be_signed_data_hash, key_handle, hash = hash(public_key), origin) // Derived key case
​    ELSE:
​    ​    sign(to_be_signed_data_hash, key_id = hash(public_key), origin) // Resident key case

Same for decrypt_wrapper.

Device API for resident keys

sign(to_be_signed_data_hash, key_id, origin) decrypt(to_be_decrypted_data, key_id, origin)

  1. Check if key_id matches any stored (resident) key and origin.
  2. Compute key operation with payload.
  3. Return result.

Device API for derived keys

sign(to_be_signed_data_hash, key_handle, hash, origin) decrypt(to_be_decrypted_data, key_handle, hash, origin)

  1. Derive private key: private_key = KDF(master key, key_handle, origin)
  2. public_key = compute_public_key(private_key) // For verification purpose
  3. compare: hash == hash(public_key) // For verification purpose
  4. Compute key operation with payload.
  5. Return result.

Advantages

Disadvantage

@szszszsz @onlykey

szszszsz commented 4 years ago

@onlykey To elaborate, this is a proposition to divide the device API (here sign and decrypt) from common key handling functions to separate ones for resident and derived keys, to limit confusion. I opt for case B, which is easier to understand than the common case A. The advantage is a simpler implementation on the device side. The wrapper in the web application should easily recognize the type of the requested key by format (size, prefix etc. TBD). For RKs, the key_id here would be OpenPGP's long id or Keybase's ID (both should be possible to be calculated by the device). Regarding the derived keys, the key_handle is a random data blob (generated on key generation, and stored by service) received from the database server, as a result of request for the data of given public key:

key_handle = server(public_key_long_id)

The database server is assumed to be maintained by the service.

onlykey commented 4 years ago

@szszszsz @jans23 Yeah I think having separate API here makes it less likely for there to be confusion (case B). If I am a web developer and I make a mistake in implementing resident key support it might just fail over to derived keys and I might think its working even though its not using the resident keys.

Also, I am not sure how you guys are looking to derive keys from origin but I was thinking this would be to use the RPID in FIDO2, unfortunately though this is different that the origin format in pre-FIDO2 so couldn't support legacy U2F only browsers.

jans23 commented 4 years ago

I'm ok with any of the two options.

szszszsz commented 4 years ago

@onlykey I believe you can get the FIDO U2F origin by just hashing the RPID in FIDO2, so in such case it should not be an issue.