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

doc: Update credentials page #454

Merged
merged 3 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 20 additions & 183 deletions docs/src/reference/creds.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,220 +10,57 @@ To simplify data management for users and developers, Greenlight uses
Credentials can be reconstructed in various ways and exported in
byte-encoded format for persistence.

## Ways to Retrieve Credentials
!!! tip
If you registered your greenlight node before the release of gl-client v0.2, please see the section [Instantiating a Credential from device certificates](#instantiating-a-credential-from-device-certificates) below.

### Nobody Identity
## Credential Types
There are two types of Credentials, the Nobody credential and the Device credential.

How to build Credentials for the *default* `Nobody` identity?
Reference instantiations of both credentials types can be found below. Complete files can be viewed from the [examples](https://github.com/Blockstream/greenlight/tree/main/examples/rust) folder from which these code snippets were taken.

=== "Rust"
```rust
use gl_client::credentials::Builder;

// Builds the default nobody identity.
let creds = Builder::as_nobody()
.with_default()
.expect("Failed to create default Nobody credentials")
.build()
.expect("Failed to build Nobody credentials");

// Access the tls config.
let tls_config = creds.tls_config()
.expect("Failed to create TlsConfig");
```
### Nobody Credentials

=== "Python"
```python
from glclient import Credentials, TlsConfig
The Nobody credentials are used when there is no registered node associated with the requests made with the credential. They can be initialized using the developer certificates acquired from the [Greenlight Developer Console][gdc] and are used for registering and recovering greenlight nodes.

# Builds the default nobody identity.
creds = Credentials.as_nobody().with_default().build();

# Access the TlsConfig
tls = TlsConfig(creds)
```

How to build Credentials for a *custom* `Nobody` identity?
[gdc]: https://greenlight.blockstream.com

=== "Rust"
```rust
use gl_client::credentials::Builder;

let ca = std::fs::read("ca.pem").expect("Failed to read from file");
let cert = std::fs::read("nobody.pem").expect("Failed to read from file");
let key = std::fs::read("nobody-key.pem").expect("Failed to read from file");

// Builds nobody credentials from custom values.
let creds = Builder::as_nobody()
.with_ca(ca)
.with_identity(cert, key)
.build()
.expect("Failed to build Nobody credentials");

// Access the tls config.
let tls_config: gl_client::tls::TlsConfig = creds.tls_config()
.expect("Failed to create TlsConfig");
--8<-- "main.rs:dev_creds"
```

=== "Python"
```python
from pathlib import Path
from glclient import Credentials, TlsConfig

capath = Path("ca.pem")
certpath = Path("nobody.pem")
keypath = Path("nobody-key.pem")

# Builds the default nobody identity.
creds = Credentials.as_nobody()
.with_ca(capath.open(mode="rb").read())
.with_identity(
certpath.open(mode="rb").read(),
keypath.open(mode="rb").read(),
)
.build()

# Access the TlsConfig
tls = TlsConfig(creds)
--8<-- "main.py:dev_creds"
```

### Device Identity

The `Credentials` for a device can be retrieved in numberous ways. They can be restored from a path to a encoded credentials file, as well as from a byte array that carries the same data. `Credentials` for the device can also be constructed by their components or a combination of all of the above.
### Device Credentials

How to build `Credentials` from encoded formats?
The Device credentials are used when there is a registered node associated with the requests made with the credential. They can be restored from a path to a encoded credentials file, as well as from a byte array that carries the same data. `Credentials` for the device can also be constructed by their components or a combination of all of the above.

=== "Rust"
```rust
use gl_client::credentials::Builder;

// Restore device credentials from a file.
let creds = Builder::as_device()
.from_path("path/to/credentials/file")
.expect("Failed to read credentials file")
.build()
.expect("Failed to build Device credentials");


// Alternatively restore from byte encoded data;
let enc_creds: [u8] = vec![...] // Some useful data here.
let creds = Builder::as_device()
.from_bytes(&enc_creds)
.expect("Faild to decode credentials")
.build()
.expect("Failed to build Device credentials");

// Access the tls config.
let tls_config = creds.tls_config()
.expect("Failed to create TlsConfig");

// Access the rune.
let rune = creds.rune();
--8<-- "main.rs:device_creds"
```

=== "Python"
```python
from glclient import Credentials, TlsConfig

# Restore device credentials from a file.
creds = Credentials.as_device()
.from_path("/path/to/credentials/file")
.build()

# Alternatively restore from byte encoded data.
creds = Credentials.as_device()
.from_bytes(b('...')) # Some meaningful data
.build()

# Access the TlsConfig
tls = TlsConfig(creds)
--8<-- "main.py:device_creds"
```

How to build `Credentials` step by step?
## Instantiating a Credential from device certificates

For glclient versions released before v0.2, device certificates were the primary mechanism used for authentication. These certificates can be upgraded by instantiating a Device credential and invoking the upgrade method with a Nobody-instantiated Scheduler and a Signer. This will give the upgrade method everything it needs to construct any missing details and will return a properly functioning Device credential.

=== "Rust"
```rust
use gl_client::credentials::Builder;

let ca = std::fs::read("ca.pem").expect("Failed to read from file");
let cert = std::fs::read("device.pem").expect("Failed to read from file");
let key = std::fs::read("device-key.pem").expect("Failed to read from file");
let rune = std::fs::read("rune").expect("Failed to read from file");

// Build device credentials step by step.
let creds = Builder::as_device()
.with_ca(ca)
.with_identity(cert, key)
.with_rune(rune)
.build()
.expect("Failed to build Device credentials");

// Access the tls config.
let tls_config = creds.tls_config()
.expect("Failed to create TlsConfig");

// Access the rune.
let rune = creds.rune();
--8<-- "main.rs:upgrade_device_certs_to_creds"
```

=== "Python"
```python
from pathlib import Path
from glclient import Credentials, TlsConfig

capath = Path("ca.pem")
certpath = Path("device.pem")
keypath = Path("device-key.pem")
runepath = Path("rune")

# Builds the default nobody identity.
creds = Credentials.as_nobody()
.with_ca(capath.open(mode="rb").read())
.with_identity(
certpath.open(mode="rb").read(),
keypath.open(mode="rb").read(),
)
.with_rune(runepath.open(mode="r").read())
.build()

# Access the TlsConfig
tls = TlsConfig(creds)
--8<-- "main.py:upgrade_device_certs_to_creds"
```

!!! tip
One can use a combination of the methods showed above to override the configuration. This example overrides the CA certificate.

=== "Rust"
```rust
use gl_client::credentials::Builder;

let ca = std::fs::read("ca.pem").expect("Failed to read from file");

// Builds the default nobody identity, overrides ca certificate.
let creds = Builder::as_nobody()
.with_default()
.expect("Failed to create default Nobody credentials")
.with_ca(ca) // CA certificate gets overriden.
.build()
.expect("Failed to build Nobody credentials");
```

=== "Python"
```python
from glclient import Credentials, TlsConfig

capath = Path("ca.pem")

# Builds the default nobody identity, overrides ca certificate.
creds = Credentials
.as_nobody()
.with_default()
.with_ca(capath.open(mode="rb").read()) # CA certificate gets overriden.
.build();

# Access the TlsConfig
tls = TlsConfig(creds)
```


[security]: ./security.md
34 changes: 22 additions & 12 deletions examples/python/getting-started/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import bip39 # type: ignore
from glclient import Credentials, Signer, Scheduler # type: ignore
import bip39 # type: ignore
from glclient import Credentials, Signer, Scheduler # type: ignore
from pathlib import Path
from pyln import grpc as clnpb # type: ignore
from pyln import grpc as clnpb # type: ignore
import secrets # Make sure to use cryptographically sound randomness


# ---8<--- [start: upgrade_device_certs_to_creds]
def upgrade_device_certs_to_creds(
scheduler: Scheduler, signer: Signer, device_cert: bytes, device_key: bytes
):
device_creds = Credentials.from_parts(device_cert, device_key, "")
return device_creds.upgrade(scheduler.inner, signer.inner)
# ---8<--- [end: upgrade_device_certs_to_creds]


def save_to_file(file_name: str, data: bytes) -> None:
with open(file_name, "wb") as file:
file.write(data)
Expand All @@ -31,16 +40,14 @@ def create_seed() -> bytes:
return seed


def nobody_with_identity(developer_cert: bytes, developer_key: bytes) -> Credentials:
ca = Path("ca.pem").open(mode="rb").read()
return Credentials.nobody_with(developer_cert, developer_key, ca)

def register_node(seed: bytes, developer_cert_path: str, developer_key_path: str) -> None:
def register_node(
seed: bytes, developer_cert_path: str, developer_key_path: str
) -> None:
# ---8<--- [start: dev_creds]
developer_cert = Path(developer_cert_path).open(mode="rb").read()
developer_key = Path(developer_key_path).open(mode="rb").read()

developer_creds = nobody_with_identity(developer_cert, developer_key)
developer_creds = Credentials.nobody_with(developer_cert, developer_key)
# ---8<--- [end: dev_creds]

# ---8<--- [start: init_signer]
Expand All @@ -55,8 +62,11 @@ def register_node(seed: bytes, developer_cert_path: str, developer_key_path: str
# ownership of the `node_id`
registration_response = scheduler.register(signer, invite_code=None)

# ---8<--- [start: device_creds]
device_creds = Credentials.from_bytes(registration_response.creds)
# save_to_file("creds", device_creds.to_bytes());
save_to_file("creds", device_creds.to_bytes())
# ---8<--- [end: device_creds]

# ---8<--- [end: register_node]

# ---8<--- [start: get_node]
Expand Down Expand Up @@ -100,9 +110,9 @@ def recover_node(developer_cert: bytes, developer_key: bytes) -> None:
# ---8<--- [start: recover_node]
seed = read_file("seed")
network = "bitcoin"
signer_creds = nobody_with_identity(developer_cert, developer_key)
signer_creds = Credentials.nobody_with(developer_cert, developer_key)
signer = Signer(seed, network, signer_creds)

scheduler = Scheduler(
network,
signer_creds,
Expand Down
Loading
Loading