Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete/update WAMP-cryptosign spec section #230

Open
oberstet opened this issue Dec 28, 2015 · 10 comments
Open

Complete/update WAMP-cryptosign spec section #230

oberstet opened this issue Dec 28, 2015 · 10 comments

Comments

@oberstet
Copy link
Member

The following describes wamp-cryptosign, a new WAMP-level authentication mechanism using the Ed25519 digital signature scheme which is based on elliptic-curve cryptography, but uses Curve25519 instead of a NIST curve like P-256.

The scheme is gaining traction as it has several attractive features both cryptographically as well as technically, and comes with a secure and high-performance implementation (NaCl, libsodium and PyNaCl).

The spec below describes an authentication scheme for WAMP based on Ed25519 with the following features:

  • public-private key based authentication
  • mutual authentication of clients and routers
  • no certificates!
  • cryptographically strong (thanks to Curve25519 crypto)

WAMP-cryptosign works using the standard WAMP opening handshake in it's authenticated variant. This message exchange looks like this:

  1. client -> router: HELLO message
  2. router -> client: CHALLENGE message
  3. client -> router: AUTHENTICATE (or ABORT message)
  4. router -> client: WELCOME (or ABORT message)

The following 4 comment sections explain the use of the WAMP messages and attributes for Ed25519-based authentication, and the steps performed by the client and the router during the authenticating opening handshake.

@oberstet
Copy link
Member Author

oberstet commented Jan 1, 2016

HELLO message

A client that wants to connect to a WAMP router using WAMP-cryptosign authentication first needs a public-private Ed25519 key pair.

With Ed25519, the private key is derived from a 32 byte random value, which when persisted, again is called private key, because the actual derivation of the private key is purely deterministic. The public key is also derived from the private key using a deterministic scheme, and again has length 32 bytes.

To connect, the client sends a [HELLO, Realm|uri, Details|dict] message to the router.

The Realm|uri can also be null to request dynamic realm assignment by the router.

The Details|dict must have the following attributes:

  • auth-methods: A list of strings with authentication methods the client is ready to perform, in descending preferrence, eg ["cryptosign"]
  • auth-id: A string with the base64 encoding of the client public key
  • auth-request-serializer: A string with one of these values "json", "msgpack" or "cbor"
  • auth-request: A string with the base64 encoding of the binary string constructed as described below.
  • auth-request-signature: A string with the base64 encoding of the client's Ed25519 signature (a 64 byte value) over the auth-request binary string.

The auth-request is constructed as follows. A dict with the following attributes is created:

  • router-pubkey: A string with the base64 encoding of the router public key the client wishes to connect.
  • client-machine-id: A string identifying the client machine (e.g. /var/lib/dbus/machine-id)
  • client-time: A string with the client time in UTC, and formatted according to ISO-8601, e.g. "2016-01-01T15:26:36.809Z"
  • client-nonce: A string with the base64 encoding of a 32 byte random nonce value.

The dict is then serialized according to the auth-request-serializer specified resulting in a binary string.

Note: the client SHOULD chose the same auth-request-serializer as the serializer in use in the underlying WAMP transport.

@oberstet
Copy link
Member Author

oberstet commented Jan 1, 2016

CHALLENGE message

A router receiving a HELLO message with a request to perform wamp-cryptosign authentication then performs the following steps:

  1. read and decode the client public key from the auth-id
  2. check the auth-request-signature versus the client public key
  3. if the signature is valid, deserialize auth-request according to auth-request-serializer
  4. lookup the router private key* that matches the given router-pubkey
  5. generate a 32 byte random nonce

*: a router might have only a single key, a single key at a time, but reissued keys from time to time, or have multiple keys valid at the same time. What matters is that there is at least the private key matching the public key requested by the client.

The router must then send a [CHALLENGE, AuthMethod|string, Extra|dict] message to the client, with AuthMethod = "cryptosign" and Extra|dict having the following attributes:

  • challenge: A string with the base64 encoding of the binary string constructed as described below.
  • challenge-signature: The base64 encoding of the client's Ed25519 signature (a 64 byte value) over the challenge binary string.

The challenge is constructed as follows. A dict with the following attributes is created:

  • client-nonce: This must be the nonce sent by the client previously in HELLO
  • router-nonce: A string with base64 encoding of a 32 byte random none generated by the server.
  • router-time (optional): A string with the router time in UTC, and formatted according to ISO-8601, e.g. "2016-01-01T15:26:36.809Z"
  • router-machine-id (optional): A string identifying the router machine (e.g. /var/lib/dbus/machine-id)

The dict is then serialized according to the auth-request-serializer, resulting in a binary string,

@oberstet
Copy link
Member Author

oberstet commented Jan 1, 2016

AUTHENTICATE message

When a client receives a CHALLENGE message from the router, it performs the following steps:

  1. check the challenge-signature versus the router public key
  2. if the signature is valid, deserialize challenge using the same serializer originally sent in HELLO
  3. verify that the client-nonce matches the one originally sent in HELLO
  4. sign the challenge using the client private key

The Ed25519 signature created (a 64 byte value) is then base64 encoded, and a [AUTHENTICATE, Signature|string, Extra|dict] message is sent to the router. The Extra|dict is the empty dict.

@oberstet
Copy link
Member Author

oberstet commented Jan 1, 2016

WELCOME message

When a router receives a AUTHENTICATE message from the client, it performs the following steps:

  1. check that AUTHENTICATE.Signature matches the expected one versus the client public key
  2. if the client has requested an explicit realm, check if the realm exists and the client is allowed to join using the client public key as client identifier
  3. if the client hasn't specified a realm, lookup the default realm for the client based on the client public key as client identifier

Send a [WELCOME, Session|id, Details|dict] message to the client.

If the realm was assigned dynamically, the Details.realm|string attribute MUST be present.

@oberstet
Copy link
Member Author

oberstet commented Jan 1, 2016

No certificates

Some words regarding certificates. WAMP-cryptosign deliberately leaves out certificates, and "only" retains the bare bones of a PKI: the public-private key pair based authentication and cryptography.

The management, distribution and possibly revocation of public keys as well as private keys, if there is any sharing of private keys across endpoints, is left to the application.

E.g. one could imagine having OpenPGP signed trust lists of public keys, or trusted root public keys hard-coded in program code, or have your public keys attached to your domain as custom DNS resource records.

@oberstet
Copy link
Member Author

oberstet commented Jan 4, 2016

Here is a log of a wamp-cryptosign authentication handshake (only the client is authenticated, and the challenge is much simplified):

(python279_1)oberstet@thinkpad-t430s:~/scm/crossbario/crossbarexamples/authentication/cryptosign/static$ python client.py --realm devices --authid client01@example.com --key client01.key
pubkey = 545efb0a2192db8d43f118e9bf9aee081466e1ef36c708b96ee6f62dddad9122
Connecting to ws://localhost:8080/ws: realm=devices, authid=client01@example.com
2016-01-05T00:53:30+0100 ClientSession connected. Joining realm <devices> under authid <client01@example.com>
2016-01-05T00:53:30+0100 TX WAMP HELLO Message (realm = devices, roles = {u'subscriber': subscriber(publisher_identification=True, payload_transparency=True, pattern_based_subscription=True, subscription_revocation=True, payload_encryption_cryptobox=True), u'publisher': publisher(publisher_identification=True, publisher_exclusion=True, payload_transparency=True, subscriber_blackwhite_listing=True, payload_encryption_cryptobox=True), u'caller': caller(payload_encryption_cryptobox=True, progressive_call_results=True, payload_transparency=True, caller_identification=True), u'callee': callee(payload_encryption_cryptobox=True, payload_transparency=True, pattern_based_registration=True, shared_registration=True, caller_identification=True, registration_revocation=True, progressive_call_results=True)}, authmethods = [u'cryptosign'], authid = client01@example.com, authrole = None)
2016-01-05T00:53:30+0100 RX WAMP CHALLENGE Message (method = cryptosign, extra = {u'challenge': 'fb645e01a5c3e54a718b5e1f91e887bdb16290e1088fc5b6e2b5230e28d690e4'})
2016-01-05T00:53:30+0100 ClientSession challenge received: Challenge(method=cryptosign, extra={u'challenge': 'fb645e01a5c3e54a718b5e1f91e887bdb16290e1088fc5b6e2b5230e28d690e4'})
2016-01-05T00:53:30+0100 TX WAMP AUTHENTICATE Message (signature = 5f336d51e8d058c08b754ad81ce76e55c4f13d0ab17f29a3f727e65c0b061ad33e47e169a0143569d5855c5eb1130e015f4ed716067599da05c60aec75e2ba06fb645e01a5c3e54a718b5e1f91e887bdb16290e1088fc5b6e2b5230e28d690e4, extra = {})
2016-01-05T00:53:30+0100 RX WAMP WELCOME Message (session = 6305273498872453, roles = {u'broker': broker(publisher_identification=True, pattern_based_subscription=True, subscription_meta_api=True, payload_encryption_cryptobox=True, payload_transparency=True, subscriber_blackwhite_listing=True, session_meta_api=True, publisher_exclusion=True, subscription_revocation=True), u'dealer': dealer(payload_encryption_cryptobox=True, payload_transparency=True, pattern_based_registration=True, registration_meta_api=True, shared_registration=True, caller_identification=True, session_meta_api=True, registration_revocation=True, progressive_call_results=True)}, realm = devices, authid = client01@example.com, authrole = device, authmethod = cryptosign, authprovider = static, authextra = None)
2016-01-05T00:53:30+0100 ClientSession joined: SessionDetails(realm=<devices>, session=6305273498872453, authid=<client01@example.com>, authrole=<device>, authmethod=cryptosign, authprovider=static, authextra=None)
2016-01-05T00:53:30+0100 TX WAMP GOODBYE Message (message = None, reason = wamp.close.normal)
2016-01-05T00:53:30+0100 RX WAMP GOODBYE Message (message = None, reason = wamp.close.normal)
2016-01-05T00:53:30+0100 ClientSession left: CloseDetails(reason=<wamp.close.normal>, message='None')
2016-01-05T00:53:30+0100 WAMP-over-WebSocket transport lost: wasClean = True, code = 1000, reason = 'None'
2016-01-05T00:53:30+0100 ClientSession disconnected
2016-01-05T00:53:30+0100 Main loop terminated.

@davidwdan
Copy link

Is there an updated spec for cryosign? This one looks like it's out of date.

@oberstet
Copy link
Member Author

@davidwdan yeah, the current text needs some work. some details, but also stuff like TLS channel binding need text

@oberstet oberstet changed the title WAMP-cryptosign Complete/update WAMP-cryptosign spec section Sep 23, 2017
@oberstet oberstet added Bug and removed Enhancement labels Sep 23, 2017
@oberstet oberstet added this to the spec-impl-coverage milestone Feb 21, 2018
@oberstet oberstet self-assigned this Feb 21, 2018
@oberstet
Copy link
Member Author

oberstet commented Apr 15, 2018

This should also have an intro section that goes into the background, differences and use for common elliptic curves:

  • secp256k1: blockchains
  • secp256r1 (aka NIST P-256, aka prime256v1): TLS
  • curve25519: upcoming

=> https://safecurves.cr.yp.to/

@konsultaner
Copy link
Contributor

konsultaner commented Feb 4, 2022

To @davidwdan and all other implementers. You can find an additional (dartlang) implementation of cryptosign here:

https://github.com/konsultaner/connectanum-dart/blob/8ec1c5f10dd9952aa839e2347f9053796ddbde37/lib/src/authentication/cryptosign_authentication.dart

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants