INTEGRITY Dokumentace

Použití Roughtime

Roughtime můžete k synchronizaci hodin použít několika způsoby. Tyto postupy využívají Balíček Go od Cloudflare ↗, který vychází z Google Go klient ↗.

Protokol je také implementován v C++ ↗, Rust ↗, a Java ↗.

Konfigurace klienta

Konfigurace klienta obsahuje seznam pojmenovaných serverů Roughtime ve formátu objektu JSON. Například:

{
  "servers": [
    {
      "name": "Cloudflare-Roughtime-2",
      "publicKeyType": "ed25519",
      "publicKey": "0GD7c3yP8xEc4Zl2zeuN2SlLvDVVocjsPSL8/Rl/7zg=",
      "addresses": [
        {
          "protocol": "udp",
          "address": "roughtime.cloudflare.com:2003"
        }
      ]
    }
  ]
}

Obsahuje u každého serveru jeho veřejný kořenový klíč. Při spuštění serveru se vygeneruje online pár veřejného a tajného klíče. Kořenový tajný klíč se používá k vytvoření delegování pro online veřejný klíč a online tajný klíč se používá k podepsání odpovědi.

Delegování plní stejnou funkci jako tradiční certifikát X.509 ↗ na webu. Klient nejprve pomocí kořenového veřejného klíče ověří delegaci a poté pomocí online veřejného klíče ověří odpověď.

Protože odpověď je auditovatelný, protokol činí každého klienta odpovědným za poskytování přesného času.

Konfigurace také určuje typ algoritmu podpisu, který server používá (aktuálně pouze Ed25519 ↗ je podporován). Konfigurace nakonec obsahuje seznam adres, na kterých je služba dostupná, a přenosový protokol, který se má pro jejich dosažení použít (aktuálně je podporováno pouze UDP).

TLS

Dobrým výchozím příkladem je synchronizace klienta nebo serveru TLS pomocí jediného serveru Roughtime. To zahrnuje výpočet časového rozdílu mezi našimi hodinami a hodinami serveru Roughtime.

Prvním krokem je načtení konfiguračního souboru (nezapomeňte importovat github.com/cloudflare/roughtime):

servers, skipped, err := roughtime.LoadConfig("roughtime.config")

V tomto příkladu proměnná servers je seznam platných konfigurací serverů získaných ze vstupního souboru. Proměnná skipped udává počet přeskočených serverů, například pokud nebyl podporován algoritmus podpisu nebo přenosový protokol.

Dále bychom získali systémový čas a dotázali se prvního serveru v seznamu:

t0 := time.Now()
rt, err := roughtime.Get(&servers[0], attempts, timeout, nil)

Tím se odešle požadavek na server a ověří se odpověď. Proměnná rt je typu *roughtime.Roughtime a představuje výsledek dotazu. Vstupy jsou:

  1. Konfigurace serveru.
  2. Počet pokusů o připojení k serveru.
  3. Doba čekání na každý pokus o připojení.
  4. Volitelný *roughtime.Roughtime, výsledek předchozího dotazu.

Pokud je poskytnut poslední parametr, použije se ke generování nonce pro požadavek (více si povíme později).

crypto/tls balíček umožňuje uživateli zadat callback ↗ pro aktuální čas použitý při ověřování certifikátů, session tiketů apod. Tento callback můžete vypočítat takto:

t1, radius := rt.Now()
delta := t1.Sub(t0.Now())
now := func() time.Time {
  return time.Now().Add(delta)
}

Proměnná t1 je čas nahlášený serverem a radius je poloměr nejistoty serveru.

Kompletní funkční příklad najdete v naší GitHub ↗.

Upozornění na ploše

Obecnějším způsobem využití Roughtime je vytvoření upozornění na ploše, která vás upozorní, když se vaše hodiny rozejdou.

V Ubuntu GNU/Linux můžete postupovat například takto:

skew := time.Duration(math.Abs(float64(delta)))
if skew > 10*time.Second {
  summary := "Check your clock!"
  body := fmt.Sprintf("%s says it's off by %v.", servers[0].Name, skew)
  cmd := exec.Command("notify-send", "-i", "clock", summary, body)
  if err := cmd.Run(); err != nil {
    // error handling ...
  }
}

Kompletní funkční příklad najdete v naší GitHub ↗ (testováno na Ubuntu 18.04). Tento program byste spouštěli jako úlohu cronu, abyste pravidelně kontrolovali, zda jsou vaše hodiny synchronizované.

Použití více zdrojů

Použití více zdrojů pro Roughtime je jednoduché (a velmi doporučené):

t0 := time.Now()
res := roughtime.Do(servers, attempts, timeout, nil)

První parametr je posloupnost serverů a zbývající parametry jsou stejné jako v roughtime.Get(). Tím se postupně dotážete každého serveru v pořadí servers v pořadí. Výstup res je slice stejné délky jako servers.

Každý prvek představuje výsledek dotazu na server. Pokud byl dotaz úspěšný, výsledek obsahuje čas serveru. Pokud úspěšný nebyl, výsledek obsahuje chybu, ke které došlo. Medián rozdílu mezi vašimi hodinami a platnými odpověďmi vypočítáte takto:

thresh := 10 * time.Second
delta, err := roughtime.MedianDeltaWithRadiusThresh(res, t0, thresh)

Tím se odmítnou odpovědi, jejichž poloměr nejistoty přesahuje 10 sekund. Pokud nebude k dispozici žádná platná odpověď, vrátí se chyba.

Audit vašich zdrojů

Funkce roughtime.Do() řetězí platné odpovědi a každý nonce generuje z odpovědi serveru z posledního úspěšného dotazu. Podrobněji to popisujeme v blog ↗, propojení dotazů tímto způsobem vytváří kryptografický důkaz, že dotazy proběhly v daném pořadí. Chcete-li ověřit, že výsledky mají tuto vlastnost, postupujte takto:

chain := roughtime.NewChain(results)
ok, err := chain.Verify(nil)
if err != nil || !ok {
  // error handling ...
}

Proměnná chain je struktura, která obsahuje první úspěšný dotaz v results. Má pole chain.Next, který ukazuje na další úspěšný dotaz. Vstupní parametr pro Verify() umožňuje použít předchozí výsledek jako výchozí bod pro ověřování řetězce. Pokud je například chain.Verify(nil) je platný, pak chain.Next.Verify(chain.Roughtime) bude platný také.

Podrobný výpis

Je možné mít roughtime.Do() vypisuje užitečné informace během provádění dotazů. Uděláte to spuštěním roughtime.SetLogger() k nastavení loggeru. Například:

roughtime.SetLogger(log.New(os.Stdout, "", 0))