Add provisioning server docs, agentbridge state, and update submodule
This commit is contained in:
BIN
.agentbridge/conversation.db
Normal file
BIN
.agentbridge/conversation.db
Normal file
Binary file not shown.
BIN
.agentbridge/conversation.db-shm
Normal file
BIN
.agentbridge/conversation.db-shm
Normal file
Binary file not shown.
BIN
.agentbridge/conversation.db-wal
Normal file
BIN
.agentbridge/conversation.db-wal
Normal file
Binary file not shown.
132
SERVER.md
Normal file
132
SERVER.md
Normal file
@ -0,0 +1,132 @@
|
||||
# Provisioning server
|
||||
|
||||
Baresip Studio can provision accounts from a server-encrypted bundle using a
|
||||
per-device RSA key pair generated in Android Keystore.
|
||||
|
||||
Endpoint contract
|
||||
- Base URL: whatever you pass as `endpoint=` in the `baresip:` URI
|
||||
- Path used by the app: `<endpoint>/bundle`
|
||||
- Method: POST
|
||||
- Request Content-Type: application/json
|
||||
- Response Content-Type: application/json
|
||||
|
||||
Enrollment request body
|
||||
{
|
||||
"extension": "101",
|
||||
"public_key": "<base64 DER SubjectPublicKeyInfo>"
|
||||
}
|
||||
|
||||
Provisioning bundle response body
|
||||
{
|
||||
"encrypted_key": "<base64 RSA-OAEP ciphertext>",
|
||||
"iv": "<base64 12-byte GCM nonce>",
|
||||
"ciphertext": "<base64 AES-GCM ciphertext>",
|
||||
"tag": "<base64 16-byte GCM tag>"
|
||||
}
|
||||
|
||||
The plaintext AES-GCM payload after decryption should be a JSON object like:
|
||||
{
|
||||
"connect_string": "sip:101@pbx.example.com",
|
||||
"transport": "tls",
|
||||
"sip_verify_server": "yes",
|
||||
"username": "101",
|
||||
"password": "secret",
|
||||
"display_name": "Extension 101",
|
||||
"account_name": "Desk Phone",
|
||||
"outbound1": "sip:pbx.example.com",
|
||||
"outbound2": "",
|
||||
"register": true,
|
||||
"reg_int": 900,
|
||||
"check_origin": true,
|
||||
"media_enc": "",
|
||||
"media_nat": "",
|
||||
"stun_server": "",
|
||||
"stun_user": "",
|
||||
"stun_pass": "",
|
||||
"rtcp_mux": false,
|
||||
"rel100": false,
|
||||
"dtmf_mode": 2,
|
||||
"answer_mode": 0,
|
||||
"auto_redirect": false,
|
||||
"vm_uri": "",
|
||||
"country_code": "",
|
||||
"tel_provider": "",
|
||||
"numeric_keypad": false,
|
||||
"default_account": true,
|
||||
"custom_params": "",
|
||||
"client_cert": "<PEM>",
|
||||
"client_key": "<PEM>",
|
||||
"ca_certs": "<PEM>"
|
||||
}
|
||||
|
||||
Python server example
|
||||
- Requirements: python >= 3.10
|
||||
- pip install fastapi uvicorn pydantic
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
import base64
|
||||
import json
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
class EnrollRequest(BaseModel):
|
||||
extension: str
|
||||
public_key: str
|
||||
|
||||
class BundleResponse(BaseModel):
|
||||
encrypted_key: str
|
||||
iv: str
|
||||
ciphertext: str
|
||||
tag: str
|
||||
|
||||
SERVER_RSA_PRIVATE_KEY_PEM = """-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----"""
|
||||
|
||||
def server_private_key():
|
||||
return serialization.load_pem_private_key(
|
||||
SERVER_RSA_PRIVATE_KEY_PEM.encode(), password=None
|
||||
)
|
||||
|
||||
@app.post("/bundle", response_model=BundleResponse)
|
||||
def bundle(req: EnrollRequest):
|
||||
pub = serialization.load_der_public_key(base64.b64decode(req.public_key))
|
||||
aes_key = AESGCM.generate_key(bit_length=256)
|
||||
aesgcm = AESGCM(aes_key)
|
||||
iv = b"012345678901" # replace with os.urandom(12) in production
|
||||
payload = {
|
||||
"connect_string": f"sip:{req.extension}@pbx.example.com",
|
||||
"transport": "tls",
|
||||
"sip_verify_server": "yes",
|
||||
"username": req.extension,
|
||||
"password": "secret",
|
||||
"client_cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
|
||||
"client_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
|
||||
"ca_certs": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
|
||||
}
|
||||
plaintext = json.dumps(payload).encode("utf-8")
|
||||
encrypted = aesgcm.encrypt(iv, plaintext, None)
|
||||
encrypted_key = server_private_key().encrypt(
|
||||
aes_key,
|
||||
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None),
|
||||
)
|
||||
ciphertext = encrypted[:-16]
|
||||
tag = encrypted[-16:]
|
||||
return BundleResponse(
|
||||
encrypted_key=base64.b64encode(encrypted_key).decode(),
|
||||
iv=base64.b64encode(iv).decode(),
|
||||
ciphertext=base64.b64encode(ciphertext).decode(),
|
||||
tag=base64.b64encode(tag).decode(),
|
||||
)
|
||||
|
||||
Example baresip URI
|
||||
baresip://provision?endpoint=https://pbx.example.com&extension=101
|
||||
|
||||
Security notes
|
||||
- Always serve over HTTPS.
|
||||
- Keep SERVER_RSA_PRIVATE_KEY_PEM secret and offline from the app.
|
||||
- Treat the bundle URL as a secret; short expiry and single-use are recommended.
|
||||
Reference in New Issue
Block a user