# Noise Keys

> UCall for Python

Interfaces and calls can run over the Noise protocol instead of plain TCP, authenticating both sides by 32-byte Curve25519 static keys.

```python
import threading

import ucall

server_private_key, server_public_key = ucall.generate_noise_keypair()
client_private_key, _ = ucall.generate_noise_keypair()
client_public_key = ucall.derive_noise_public_key(client_private_key)

sealed = ucall.Interface(
    "127.0.0.1:0",
    security=ucall.Security.NOISE_XX,
    noise_private_key=server_private_key,
    peer_authorizer=lambda key: key == client_public_key,
)
server = ucall.Server(sealed)

@server.get("/whoami")
def whoami(request: ucall.Request):
    _, static_key = request.peer
    return static_key

runner = threading.Thread(target=server.run)
runner.start()
client = ucall.Client(
    connections=1,
    noise_private_key=client_private_key,
    peer_authorizer=lambda key: key == server_public_key,
)
response = client.get(f"http+noise://{server.addresses[0]}/whoami")
server.stop()
runner.join()
assert bytes(response.body) == client_public_key
```

`generate_noise_keypair` draws from the operating system's entropy and raises `ucall.Error` with `Status.IO` when it is refused.
`derive_noise_public_key` recovers the public half of a stored private key, which peers compare in their authorizers.
Under Noise XX both sides learn each other's key during the handshake, so both need an authorizer; Noise IK has the client name the server's key in advance through `noise_peer_key=`.
Noise KK has both sides name each other in advance: the interface admits one caller through `noise_peer_key=`, and the call names its own private key with `noise_private_key=` and the server's with `noise_peer_key=`.
An `http+noise://` URL implies Noise XX, and a handler reads the client's authenticated key through `request.peer`.

`import ucall`

## Security

Transport encryption of an [`Interface`](https://www.unum.cloud/docs/ucall/python/serving.md#Interface) or a client call: plaintext or a Noise pattern.

```python
class Security(enum.IntEnum)
```

- `NONE` = `0`
- `NOISE_XX` = `1`
- `NOISE_IK` = `2`
- `NOISE_KK` = `3`

Examples: ```
>>> ucall.Security["NOISE_XX"]
&#x3C;Security.NOISE_XX: 1>
```

## generate_noise_keypair

Draws a fresh Noise identity from the operating system's entropy.

```python
def generate_noise_keypair() -> tuple[bytes, bytes]
```

Raises: - [`Error`](https://www.unum.cloud/docs/ucall/python/errors.md#Error): The operating system refused the entropy.

Examples: ```
>>> private_key, public_key = ucall.generate_noise_keypair()
>>> len(private_key), len(public_key)
(32, 32)
```

## derive_noise_public_key

Recovers the public half of a stored Noise private key.

```python
def derive_noise_public_key(private_key: bytes, /) -> bytes
```

- `private_key`: The 32-byte private key.

Raises: - `ValueError`: `private_key` is not 32 bytes long.

Examples: ```
>>> private_key, public_key = ucall.generate_noise_keypair()
>>> ucall.derive_noise_public_key(private_key) == public_key
True
```

