INTEGRITY Документация

Использование Roughtime

Существует несколько способов использовать Roughtime для синхронизации часов. В этих примерах используется Пакет Go от Cloudflare ↗, основанный на разработке Google Go клиент ↗.

Протокол также реализован на C++ ↗, Rust ↗, и Java ↗.

Настройка клиента

Конфигурация клиента представляет собой список именованных серверов Roughtime, оформленный в виде объекта JSON. Например:

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

В нее входят данные каждого сервера корневой открытый ключ. При запуске сервер генерирует онлайн пару открытого и секретного ключей. Корневой секретный ключ используется для создания делегирование для онлайн-открытого ключа, а онлайн-секретный ключ используется для подписи ответа.

Делегирование выполняет ту же функцию, что и традиционный сертификат X.509 ↗ в вебе. Сначала клиент использует корневой открытый ключ для проверки делегирования, а затем онлайн-открытый ключ для проверки ответа.

Поскольку ответ поддающийся проверке, протокол обязывает каждого клиента отвечать за предоставление точного времени.

В конфигурации также указывается тип алгоритма подписи, который использует сервер (в настоящее время поддерживается только Ed25519 ↗ поддерживается). Наконец, конфигурация содержит список адресов, по которым доступен сервис, и указание транспортного протокола для обращения к ним (в настоящее время поддерживается только UDP).

TLS

Хорошим первым примером будет синхронизация клиента или сервера TLS с использованием одного сервера Roughtime. Для этого нужно вычислить разницу во времени между вашими часами и часами сервера Roughtime.

Первый шаг: загрузить файл конфигурации (не забудьте импортировать github.com/cloudflare/roughtime):

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

В этом примере переменная servers представляет собой список корректных конфигураций серверов, полученных из входного файла. Переменная skipped указывает количество пропущенных серверов, например если алгоритм подписи или транспортный протокол не поддерживается.

Далее нужно получить системное время и обратиться к первому серверу в списке:

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

Это отправляет запрос на сервер и проверяет ответ. Переменная rt имеет тип *roughtime.Roughtime и представляет собой результат запроса. Входные данные:

  1. Конфигурация сервера.
  2. Количество попыток подключения к серверу.
  3. Время ожидания для каждой попытки подключения.
  4. Необязательный *roughtime.Roughtime, результат предыдущего запроса.

Если передан последний параметр, он используется для генерации nonce для запроса (подробнее об этом далее).

crypto/tls позволяет пользователю указать обратный вызов ↗ для получения текущего времени при проверке сертификатов, session ticket и так далее. Вычислить этот callback можно следующим образом:

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

Переменная t1 это время, сообщённое сервером, а radius обозначает радиус неопределённости сервера.

Полный рабочий пример можно найти в нашем GitHub ↗.

Оповещения на рабочем столе

Более универсальный способ использования Roughtime заключается в создании оповещений на рабочем столе, которые предупреждают вас о рассинхронизации часов.

В Ubuntu GNU/Linux это можно сделать следующим образом:

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 ...
  }
}

Полный рабочий пример можно найти в нашем GitHub ↗ (протестировано на Ubuntu 18.04). Эту программу можно запускать как cron-задание, чтобы периодически проверять синхронизацию часов.

Использование нескольких источников

Использовать несколько источников для Roughtime легко, и это настоятельно рекомендуется:

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

Первый параметр представляет собой последовательность серверов, а остальные параметры совпадают с параметрами в roughtime.Get(). При этом опрашивается каждый сервер в последовательности servers по порядку. Результат res представляет собой срез той же длины, что и servers.

Каждый элемент представляет собой результат запроса к серверу. Если запрос выполнен успешно, результат содержит время сервера. Если нет, результат содержит возникшую ошибку. Чтобы вычислить медианную разницу между вашими часами и корректными ответами, выполните следующее:

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

При этом отклоняются ответы, у которых радиус неопределенности превышает 10 секунд. Если ни одного действительного ответа не получено, возвращается ошибка.

Аудит источников

Функция roughtime.Do() связывает воедино корректные ответы, формируя каждый nonce на основе ответа сервера из последнего успешного запроса. Подробнее об этом рассказано в блог ↗, такая связь запросов друг с другом даёт криптографическое доказательство того, что запросы были выполнены по порядку. Чтобы проверить это свойство результатов, выполните следующее:

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

Переменная chain представляет собой структуру, содержащую первый успешный запрос в results. У него есть поле chain.Next, которая указывает на следующий успешный запрос. Входной параметр для Verify() позволяет использовать предыдущий результат как отправную точку для проверки цепочки. Например, если chain.Verify(nil) действителен, то chain.Next.Verify(chain.Roughtime) также будет действителен.

Подробный вывод

Можно настроить roughtime.Do() выводит полезную информацию по ходу выполнения запросов. Для этого вызовите roughtime.SetLogger() для настройки логгера. Например:

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