205б. Лицензии открытого кода и SBOM: что можно взять в прошивку
Раздел 205а собрал на прибор документы по ЕСКД: схему, перечень элементов, спецификацию. В перечне — каждый резистор. А в прошивке того же прибора обычно больше чужого кода, чем своего: CMSIS, HAL, FreeRTOS, файловая система, стек TCP/IP, библиотеки из разделов 42, 42а, 106в и 115г. У каждого куска свой владелец и своя лицензия, и лицензия говорит, на каких условиях этот код можно передавать дальше — в том числе внутри прибора. Здесь — какие лицензии у кода, который уже встречался в справочнике, что в них написано и как составить перечень программных составных частей прошивки (SBOM, software bill of materials) так же строго, как перечень элементов.
Это не юридическая консультация. Раздел пересказывает только то, что написано в самих текстах лицензий, лежащих на диске, с номерами пунктов. Как их применять к конкретному прибору и договору — вопрос к юристу.
Опоры: архив STM32CubeF1 1.8.7 (License.md, Package_license.md,
sbom_cdx.json, файлы LICENSE* компонентов, текст SLA0044 в
Projects/…/Applications/LICENSE.txt); модули дерева Zephyr (littlefs,
nanopb, zcbor, Mbed TLS 3.6) и генератор SPDX Zephyr
(scripts/west_commands/zspdx); тексты GPL-2.0, LGPL-3.0 и исключения GCC
для библиотеки времени выполнения — из zephyr-sdk-1.0.1. Всё
сопоставление — свой генератор sbom205.py; проверка —
_verify/lic205c_check.py.
Как это называется в документации
Оригинал Перевод permissive license разрешительная лицензия: условия — сохранить уведомление, приложить текст copyleft «копилефт»: производное передаётся на тех же условиях, с исходниками notice / attribution уведомление об авторском праве и тексте лицензии object code / binary form объектный код, двоичная форма — в том числе прошитая в прибор combined work (LGPL) совместное произведение: программа, связанная с библиотекой runtime library exception исключение для библиотеки времени выполнения (GCC) SBOM перечень программных составных частей SPDX license identifier короткое имя лицензии по списку SPDX: MIT,BSD-3-Clauselicense expression выражение: Apache-2.0 OR GPL-2.0-or-laterLicenseRef-…своё имя для лицензии, которой нет в списке SPDX
205б.1 Что уже взято и под какими лицензиями
Опыт: дерево исходников прибора собрано из архивов на диске — 12
компонентов, 39 файлов, 2 490 629 байт. Лицензию каждого файла генератор
определил по его заголовку, лицензию компонента — по его файлу LICENSE:
| Компонент | Версия | Лицензия | Откуда версия |
|---|---|---|---|
| CMSIS-Core | 5.4.0 | Apache-2.0 | ARM.CMSIS.pdsc |
| CMSIS-Device STM32F1 | 4.3.5 | Apache-2.0 | stm32f1xx.h |
| HAL STM32F1 | 1.1.10 | BSD-3-Clause | stm32f1xx_hal.c |
| FreeRTOS | 10.3.1 | MIT | task.h |
| USB Device Library ST | 2.5.3 | SLA0044 | Release_Notes.html |
| FatFs | R0.11 | своя, одно условие (ff.c); ST, пять условий (ff_gen_drv.c) |
ff.h |
| lwIP | 2.0.3 | BSD-3-Clause | init.h |
| littlefs | 2.11 | BSD-3-Clause | lfs.h |
| nanopb | 0.4.9 | Zlib | library.json |
| zcbor | 0.9.1 | Apache-2.0 | VERSION |
| Mbed TLS | 3.6.6 | Apache-2.0 OR GPL-2.0-or-later | build_info.h |
| свой код прибора | 1.4.2 | своя (LicenseRef-Proprietary) |
— |
У HAL и CMSIS-Device в заголовке файла лицензии нет — только фраза, что
условия лежат в файле LICENSE компонента. А тот говорит: внутри пакета
действуют условия Package_license, вне пакета — BSD-3-Clause
(для HAL) и Apache-2.0 (для CMSIS-Device). Package_license.md пакета
F1 — это соглашение ST SLA0048, и в его таблице эти компоненты
перечислены с BSD-3-Clause и Apache-2.0.
Компонент FatFs показывает, что лицензию надо читать по файлам, а не
по названию папки: ff.c от ChaN несёт лицензию с одним условием, а
лежащий рядом ff_gen_drv.c от ST — лицензию с пятью условиями, и
четвёртое требует исполнять код только на кристаллах ST.
205б.2 Что написано в разрешительных лицензиях
Главный вопрос для прошивки: что требует лицензия, когда код передаётся в двоичной форме — прошитым в прибор. Пересказ по текстам на диске:
| Лицензия | Что сказано о двоичной форме | Где в тексте |
|---|---|---|
| Apache-2.0 (CMSIS, zcbor, Mbed TLS) | распространять можно «in Source or Object form» при условиях: получатель получает копию лицензии, изменённые файлы помечены, уведомления сохранены, файл NOTICE, если он есть, передаётся |
п. 4 (a)–(d) |
| MIT (FreeRTOS) | уведомление и текст разрешения включаются во все копии или существенные части | единственный абзац условий |
| BSD-3-Clause (lwIP, littlefs, HAL вне пакета) | в двоичной форме — воспроизвести уведомление, условия и отказ от гарантий в документации или других материалах | условие 2 |
| Zlib (nanopb) | упоминание в документации продукта приветствуется, но не обязательно; изменённые исходники помечать | условия 1 и 2 |
| FatFs R0.11 (ChaN) | условие одно — для исходного кода: сохранить уведомление | условие 1 |
Файла NOTICE в каталогах CMSIS, zcbor и Mbed TLS на диске нет. Для
разрешительных лицензий вывод из текстов один и тот же: код можно
передавать внутри прибора, но в документации к прибору должны быть
уведомления и тексты лицензий тех компонентов, которые этого требуют.
Генератор собирает эти тексты в один файл LICENSES-THIRD-PARTY.txt
(58 774 байта для девяти компонентов с файлом LICENSE) — его
прикладывают к руководству.
Лицензии ST. SLA0044 (текст — в LICENSE.txt примеров, Rev6,
октябрь 2025) разрешает передачу в двоичной форме без уведомления, если
код встроен в кристалл ST (условие 2), требует исполнять код только на
кристаллах ST или вместе с ними (условие 4) и запрещает такое
использование, при котором код оказался бы под условиями открытых
лицензий — GPL, EPL, Apache, BSD, MIT перечислены прямо (условие 5).
SLA0048 пакета содержит те же по смыслу условия 2, 4 и 5.
205б.3 GPL и LGPL в прошивке
У копилефт-лицензий условие другое: вместе с двоичным кодом — исходники.
- GPL-2.0, п. 3 (текст
COPYING.GPL2из picolibc): объектный код программы, основанной на коде под GPL, можно распространять, если приложить полный машиночитаемый исходный текст или письменное предложение его выдать. Прошивка, в которую скомпилирован такой код, — это и есть объектный код. - LGPL-3.0, п. 4 (
COPYING3.LIBиз gcc): совместное произведение можно передавать на своих условиях, если, среди прочего, выполнить одно из двух: передать исходники библиотеки и код приложения в виде, позволяющем пересобрать его с изменённой библиотекой, — или использовать разделяемую библиотеку, уже установленную у пользователя. В прошивке микроконтроллера разделяемых библиотек нет — остаётся первый путь: отдать объектные файлы приложения для пересборки. - Исключение GCC для библиотеки времени выполнения
(
COPYING.RUNTIME): код, сгенерированный компилятором, вместе с файлами библиотеки времени выполнения, на которых стоит это исключение, можно передавать на своих условиях, даже если иначе это нарушило бы GPLv3, — при условии, что весь код получен «допустимым процессом компиляции». Это место в текстах касается любой прошивки, которую компоновщик gcc собрал вместе со своей библиотекой. - Выбор получателя. Mbed TLS выдан по выражению
Apache-2.0 OR GPL-2.0-or-later: файлLICENSEпрямо говорит, что пользователь сам выбирает одну из двух. В SBOM это выражение пишется как есть, а в документах выпуска — какая из двух выбрана.
Отдельно — столкновение с лицензиями ST. Условие 5 SLA0044 запрещает использовать код ST так, чтобы он попал под открытые условия; GPL в нём названа прямо. Код под GPL и библиотека USB от ST в одной прошивке — это два текста с противоположными требованиями к одному и тому же образу.
205б.4 SBOM: два формата и два готовых примера на диске
SBOM — таблица составных частей программы: имя, версия, поставщик, лицензия, контрольная сумма. Форматов два, оба — JSON:
- SPDX 2.3 — описание пакетов и каждого файла с контрольными
суммами SHA-1 и SHA-256, лицензия файла (
licenseInfoInFiles, что написано в самом файле) отдельно от вывода (licenseConcluded), код проверки пакета — SHA-1 от отсортированных SHA-1 его файлов; - CycloneDX 1.5 — компоненты с версиями и лицензиями, вложенные компоненты-файлы с SHA-256, зависимости.
Готовые примеры уже есть на диске. ST кладёт в архив STM32CubeF1 файл
sbom_cdx.json — CycloneDX 1.5, 46 компонентов, сделан сторонней
программой анализа. Zephyr несёт свой генератор SPDX
(west spdx, каталог zspdx): пишет SPDX 2.3 в текстовом виде, список
лицензий SPDX версии 3.12 (499 идентификаторов), а лицензию файла ищет
только по метке SPDX-License-Identifier: в первых 20 строках.
Этого мало для кода ST и старых библиотек: из 39 файлов опыта метка SPDX есть у 13. Остальным нужен разбор текста заголовка.
205б.5 Свой генератор: по дереву исходников на ПК
sbom205.py собирает дерево прибора из архивов на диске и описывает его.
Лицензию файла он ищет в первых 80 строках — сначала метку SPDX, потом
известные тексты:
def detect(text):
"""Лицензия по тексту заголовка или файла LICENSE. None — ничего не сказано; 'SEE-LICENSE' — отсылка к файлу."""
m = re.search(r"SPDX-License-Identifier:\s*(.+)", text)
if m:
return m.group(1).strip().rstrip("*/").strip()
t = re.sub(r"\s+", " ", re.sub(r"^[ \t]*[*/#;]+ ?", "", text, flags=re.M))
if "Ultimate Liberty license SLA0044" in t or "terms of the SLA0044 license shall apply" in t:
return "LicenseRef-ST-SLA0044"
Порядок важен. Пятипунктная лицензия ST начинается теми же словами, что BSD, — «Redistribution and use in source and binary forms», — поэтому сначала ищется признак «только кристаллы ST», и лишь потом считаются три условия BSD:
if "must execute solely and exclusively on microcontroller" in t:
return "LicenseRef-ST-5-Clause" # похоже на BSD, но условий пять, и четвёртое — только кристаллы ST
Итог по 39 файлам: метка SPDX — 13, узнанный текст — 13, отсылка к
LICENSE компонента — 9, в заголовке ничего — 4 (три файла nanopb и
pbuf.c из lwIP, у которого текст лицензии начинается с 81-й строки).
Если файл молчит, берётся лицензия компонента из его LICENSE; если нет
и её — файл получает NOASSERTION и выпуск останавливается.
Так и вышло в первом прогоне: в своём коде лежал app/ringbuf.c,
взятый «откуда-то», без строки о лицензии. Генератор ответил
нет лицензии: ./app/ringbuf.c, итог компонента — NOASSERTION.
После добавления строки SPDX-License-Identifier: ошибок 0.
На выходе три файла: sbom.spdx.json, sbom.cdx.json и
LICENSES-THIRD-PARTY.txt. Дата внутри постоянная, пространство имён
SPDX и серийный номер CycloneDX выводятся из контрольных сумм файлов —
два прогона дали отчёты, совпавшие байт в байт. SBOM воспроизводим так
же, как прошивка в разделе 201а, и его можно хранить рядом с
merged.hex в журнале выпусков.
205б.6 Сверка: все файлы найдены, лицензии совпали
Проверка раздела сверяет отчёт с деревом и с двумя чужими реализациями:
| Что сверялось | Итог |
|---|---|
| файлов в дереве / в SPDX | 39 / 39, у каждого SHA-1 и SHA-256 пересчитаны |
| код проверки 12 пакетов | совпал с функцией calculateVerificationCode из zspdx |
| идентификаторы лицензий | 5 — в списке SPDX 3.12 из Zephyr; 4 — LicenseRef-… с пояснением в hasExtractedLicensingInfos |
| метка SPDX в первых 20 строках (сканер Zephyr) | 13 файлов, у всех тот же ответ, что у своего генератора |
| лицензия в заголовке ↔ лицензия компонента | расхождений 0 |
SBOM от ST (sbom_cdx.json) |
7 общих компонентов: версии совпали у всех, лицензии — у 6 |
Расхождение одно — FatFs: у ST в SBOM BSD-3-Clause, а в заголовке
ff.c той же версии R0.11 — одно условие, в ff_gen_drv.c — пять
условий ST. Чей SBOM «прав» — вопрос не к генератору; генератор показал,
что написано в файлах, и указал место расхождения.
Сканер Zephyr в окружении Windows сначала нашёл не 13, а 11 файлов: он
открывает файл в кодировке системы (здесь cp1251), и два файла своего
кода с русскими комментариями в UTF-8 не прочитались. С PYTHONUTF8=1
— 13.
205б.7 Грабли
«Лицензия как у папки». В одной папке FatFs — лицензия с одним
условием и лицензия ST с пятью. SBOM строится по файлам, а итог
компонента — выражение LicenseRef-FatFs-ChaN AND LicenseRef-ST-5-Clause.
«Похоже на BSD». Пятипунктная лицензия ST начинается словами BSD.
Поиск по первой фразе запишет её как BSD-3-Clause и потеряет условие
«только кристаллы ST». Признаки ограничений проверяются раньше общих.
Лицензия ниже двадцатой строки. У pbuf.c текст начинается с 81-й
строки, после описания модуля. Сканер по первым строкам его не видит;
выручает LICENSE компонента — если он есть.
Файл «откуда-то». Кусок кода без строки о лицензии — самая частая
дыра. Генератор должен останавливать выпуск на NOASSERTION, а не
молча приписывать лицензию проекта.
Два номера соглашения ST в одном пакете. License.md называет у
библиотеки USB соглашение SLA0044, Package_license.md того же пакета —
SLA0048, файлы библиотеки — SLA0044. В SBOM пишется то, что в файле, а
расхождение отмечается.
Версия «из заголовка». У nanopb в pb.h записано
nanopb-1.0.0-dev, а в library.json — 0.4.9. У CMSIS
cmsis_version.h даёт 5.1 (версия ядра CMSIS-Core), а описание пакета —
5.4.0. В SBOM указывают, откуда взята версия, и берут её из одного и
того же места у всех выпусков.
Кодировка у сканера. Чужой сканер, читающий файлы в кодировке системы, пропускает файлы с русскими комментариями в UTF-8 — без ошибки, просто «лицензия не найдена».
GPL «через зависимость». Mbed TLS позволяет выбрать GPL-2.0, но
выбор делает получатель кода. Если в документах выпуска выбор не
записан, SBOM с выражением OR остаётся вопросом, а не ответом.
205б.8 Итоги
- Дерево прибора — 12 компонентов, 39 файлов: лицензии Apache-2.0, MIT,
BSD-3-Clause, Zlib, одноусловная FatFs, SLA0044, пятипунктная ST и
Apache-2.0 OR GPL-2.0-or-laterу Mbed TLS. - Разрешительные лицензии не запрещают прошивать код в прибор; BSD-3, MIT и
Apache-2.0 требуют уведомлений и текста лицензии в документации —
LICENSES-THIRD-PARTY.txtна 58 774 байта. - Лицензии ST разрешают передачу в двоичной форме внутри кристалла ST, но требуют исполнять код только на кристаллах ST и запрещают смешение с открытыми лицензиями.
- GPL-2.0 требует исходники при передаче объектного кода; LGPL-3.0 без разделяемых библиотек — возможность пересобрать приложение; исключение GCC снимает условия GPLv3 с библиотеки времени выполнения компилятора при «допустимом процессе компиляции».
- Свой генератор: метка SPDX у 13 файлов из 39, узнанный текст — у 13,
отсылка к
LICENSE— у 9, ничего — у 4; файл без лицензии останавливает выпуск. - Сверка: 39 из 39 файлов, коды проверки 12 пакетов = функция Zephyr, версии 7 общих компонентов = SBOM от ST, лицензии — у 6 из 7 (кроме FatFs).
Дальше: раздел 206 «Сертификация: подтверждение соответствия и радиомодули».