# `web5-sdk-kotlin`
## Design Motivations
### Ezpz from the Consumer's POV
DIDs and Verifiable Credentials are notoriously difficult for developers to start making use of because of a steep learning curve; most of which stems from cryptographic primitives. Given that tbDEX and Web5 both heavily rely upon DIDs and VCs, i think it'd be in our best interest to make usage of our SDKs as simple as we possibily can.
Devs should be able to focus on their own business logic as soon as possible vs. being encumbered by a bunch of kakamimi that we can take care of. This can be achieved by providing sensible defaults and single line methods to achieve whatever they need to do (e.g. DID creation, signing, verifying)
Can't have people getting stuck for days on how to create & use a DID or sign a tbDEX message. People should be able to do that within minutes when they're getting started.
IMO, the primary focus is to produce an SDK that helps developers achieve their intended outcome (e.g. standing up PFIs, wallets etc.) with as little friction as possible. The more friction there is, the less likelihood there is that tbDEX spreads far and wide.
### Security as a First Class Citizen
sensible defaults and one-liner methods are great for the prototyping phase. Successful prototypes lead motivate production deployments and as organizations move towards production, security becomes incredibly important.
Lack of support for InfoSec requirements can easily prevent PFIs or wallets from making their way to the real world. Ensuring that we provide the functionality needed for dev teams to transition from yolo prototypes to hardened production readiness is critically important
### Multiple Runtime Compatibility
we have the potential to support:
* Kotlin server-side
* Java server-side
* Android
While we don't necessarily have the time to deterministically ensure that everything works perfectly across all 3 runtimes right at this moment, we should keep it in the back of our minds.
This starts with refraining from kotlin **neckbeardery**. The longer our neckbeards, the less likely we are to produce an SDK that works when used in java projects.
We can also keep an eye on the dependencies we include to ensure that they either:
* are known to work in java
* have counterpart dependencies that can be hotswapped in via gradle at build time (e.g. `google.tink` for crypto stuff)
## SDK Architecture
[`web5-sdk-kotlin`](https://github.com/TBD54566975/web5-sdk-kotlin) a multi-module gradle monorepo that consists of the following modules:
| Module | Description | Dependencies |
| ------------- | --------------------------------------------------------------------------- | -------------------------- |
| `common` | utilities for encoding, decoding, and hashing | N/A |
| `crypto` | key generation, signing, signature verification, encryption, and decryption | `common` |
| `dids` | did generation and resolution | `common`, `crypto` |
| `credentials` | creation / verification of verifiable claims & Presentation Exchange | `common`, `crypto`, `dids` |
* [common](./common) - utilities for encoding, decoding, and hashing
* [crypto](./crypto) - key generation, signing, signature verification, encryption, and decryption
* [dids](./dids) - did generation and resolution
* [credentials](./credentials) - creation and verification of verifiable claims
Each module will be published to maven central individually under `web5:$pkg-name:$version`. Additionally we'll have a wombo combo that includes all JARs under `web5:sdk:$version`
Rationale behind the multi-module approach is:
* allows us to swap out / make adjustments to smaller surface areas when tackling different runtimes
* specifically with respect to the `crypto` package, when/if we go down the path of having our libs audited, there's a smaller surface area to audit if we consolidate all of our crypto to a single package. similarly so, if a security vulnerability crops up in one of the underlying dependencies, we can version bump, publish a release, and have all of our downstream packages version bump a single package. not 2 or 3.
* nice to be able to pull functionality as needed off the shelf and run with it
## Module Design
### `crypto` Module
This module sort of sets the stage for every other module given that `dids`, `credentials`, and the tbDEX SDK all have to either generate keys, sign, and verify stuff.
#### Interfaces
##### [`CryptoPrimitive`]()
`CryptoPrimitive` defines an interface that is concretely implemented for each cryptographic primitive we intend to support (specifically `Ed25519` and `Secp256k1`). The interface defines functions for:
* key generation (public & private) as `ByteArray`s and `JWK`s
* `ByteArray` often needed for DID creation
* signing
* verifying
* conversion between `ByteArray` keys <-> `JWK` representation and vice versa
This interface includes `generatePrivateKey(): ByteArray` and `generatePrivateKeyJwk(): JWK` functions that take no args. the purpose of the no argument functions are to inforce each concrete implementation to provide a way to generate keys without having to think or know about what options might exist. concrete implemenations of these functions can use sensible defaults to call `generatePrivateKey(options)` or `generatePrivateKeyJwk(options)` respectively
##### [`KeyManager`]()
`KeyManager` defines an interface that is concretely implemented to:
* generate & store keys
* retreive keys by alias (or ID)
* sign using stored keys
`KeyManager` implementations are what's passed around to higher order methods in order to generate keys and sign things (e.g. did creation, VC signing, tbdex message signing).
Examples of concrete `KeyManager` implementations are:
* `InMemoryKeyManager` - Yolo style in memory keystore under the hood. great for prototyping and getting started
* `AWSKeyManager` - leverages AWS KMS via the AWS SDK under the hood to securely generate keys and sign
* `GCPKeyManager` - leverages GCP's KMS offering via the GCP sdk under the hood to securely generate keys and sign
#### Implementations
##### `InMemoryKeyManager`
Yolo style in memory keystore (just a hashmap of key alias -> JWK) under the hood. great for prototyping and getting started
Leverages concrete implementations of `CryptoPrimitive` to generate keys, sign and verify
##### `Ed25519`
concrete implementation of `CryptoPrimitive` used to generate `Ed25519` keys. sign with them, and verify `Ed25519` signatures
##### `Secp256k1`
concrete implementation of `CryptoPrimitive` used to generate `Secp256k1` keys. sign with them, and verify `Secp256k1` signatures
### `dids` Module
this module contains shiz needed to generate and resolve DIDs.
#### Interfaces
##### [`DidMethod`]()
This interface includes:
* `create(): Did`
* function that takes no args. the purpose of the no argument function is to encourage each concrete implementation to provide a way to generate a DID without having to think or know about what options might exist. concrete implemenations of these functions can use sensible defaults to call `create(options)`. See [`DidKey.create`]() as an example
* `create(options): Did`
* `resolve(did): DidResolutionResult`
Example (mostly non-existent) concrete implementations:
* `DidKey`
* `DidIon`
* `DidWeb`
* `DidJwk`
* `DidPk`
#### Implementations
##### `DidKey`
> TODO: Fill out
## Example Usage
Here's a few examples of how the web5-sdk + tbdex-sdk can be used
### Prototype Phase
Example of what things might look like as people start to prototype
```kotlin=
val did = DidKey.create() // could be DidIon, DidJwk, DidWeb, or whatever DID methods we have implemented / available. can BYO
val rfq = Rfq(/* args to create rfq */)
rfq.sign(did)
val response = TbdexHttpClient.sendMessage(rfq)
println("response: $response")
```
In the above example:
* a DID is created with no args. an `InMemoryKeyManager` is used under the hood and returned with the did
* a tbDEX RFQ is created
* RFQ is signed using keys associated to the did passed to `sign`
* RFQ is sent to a PFI
### Production Hardened
```kotlin=
val keyManager = AwsKeyManager() // could be GcpKeyManager() or whatever KeyManager implementations we have available. can BYO
val did = DidIon.create(keyManager = keyManager)
val rfq = Rfq(/* args to create rfq */)
rfq.sign(did)
val response = TbdexHttpClient.sendMessage(rfq)
println("response: $response")
```
in the above example:
* A DID is created but the private key(s) are created directly in AWS KMS.
* a tbdEX RFQ is created
* RFQ is signed using the underlying key manager that houses the keys for the provided DID
* RFQ is sent to a PFI
#### Using Pre-existing DID
```kotlin=
val keyManager = AwsKeyManager()
val did = DidIon(did = "PRE-EXISTING_DID", keyManager = keyManager)
val rfq = Rfq(/* args to create rfq */)
rfq.sign(did)
val response = TbdexHttpClient.sendMessage(rfq)
println("response: $response")
```
in the above example:
* a pre-existing DID is instantiated along with `KeyManager` that houses the keys associated to that DID
* a tbdEX RFQ is created
* RFQ is signed using the underlying key manager that houses the keys for the provided DID
* RFQ is sent to a PFI
you can imagine creation / signing of Verifiable Credentials working the same way as tbDEX message creation and signing work since both tbdex messages and verifiable credentials are both just... wait for it... json objects!