Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,28 @@ The application supports:
- EDDSA with Ed25519 curve
- ECDH with secp256R1, secp256K1 and curve25519 curves

> [!CAUTION]
> **⚠️ Data loss on App or OS update.**
>
> All keys and metadata stored on the device are wiped whenever the App is reinstalled,
> which happens during any App update and during any OS update.
> A backup/restore script is provided, but it is **not trivial to use and does not work in every scenario**.
> See [Known limitations](#known-limitations) before you generate keys you cannot afford to lose.

## Table of Contents

- [Installation and Usage](#installation-and-usage)
- [Add-on](#add-on)
- [Key slot](#key-slot)
- [Seeded key generation](#seeded-key-generation)
- [On screen reset](#on-screen-reset)
- [Quick start guide](#quick-start-guide)
- [Compilation and load](#compilation-and-load)
- [Tests](#tests)
- [Documentation](#documentation)
- [Continuous Integration](#continuous-integration)
- [Known limitations](#known-limitations)

## Installation and Usage

See the full doc in [rst](doc/user/app-openpgp.rst), or in [pdf](<https://github.com/LedgerHQ/app-openpgp/blob/master/doc/user/app-openpgp.pdf>)
Expand Down Expand Up @@ -52,7 +74,10 @@ In this mode, key material is generated from the global token seed.

Also, a backup/restore mechanism is provided. Please report to the [Documentation](#documentation).

> Warning: Without such configuration, an OS or App update will cause your private key to be lost!"
> [!WARNING]
> **⚠️ Without one of these mechanisms in place, an OS or App update will wipe your private keys.**
>
> See [Known limitations](#known-limitations) for the full picture.

The following is a repeatable process that will generate the same keys and fingerprints
(even with different card serial numbers).
Expand Down Expand Up @@ -325,9 +350,36 @@ It outputs 3 artifacts:

## Known limitations

Today, the current App has no documented limitations.
### Data loss on App or OS update

> [!CAUTION]
> **⚠️ On-device data is wiped on every App or OS update.**
>
> The OpenPGP App stores all keys and metadata in the App's own non-volatile memory.
> Anything generated or imported on the device — private keys, fingerprints, key templates,
> cardholder data, PIN counters, etc. — is **erased** whenever the App is reinstalled.
> This happens in two common situations:
>
> - when the App itself is updated to a new version,
> - when the device OS is updated, since the App is reinstalled as part of the OS update flow.
>
> A backup/restore script is shipped in [`pytools/`](pytools/) and documented in the
> [user documentation](doc/user/app-openpgp.rst).
> It is, however, **not trivial to operate and is not guaranteed to work in every scenario**
> (in particular, private-key restore only works if **SEED mode** was enabled when the backup was taken).
>
> Until a permanent on-device storage mechanism is available, **using the OpenPGP App in production
> requires that you are comfortable with this constraint**. Concretely:
>
> - enable **SEED mode** before generating any key, so keys can be regenerated deterministically,
> - take a backup with the provided script **before every App or OS update**,
> - never store on the device a key you cannot afford to lose or regenerate.
>
> This limitation is structural and is not specific to a given release.

### Historical Bytes removed from `GET_DATA`

But, we have removed the **Historical Bytes** from the initial `GET_DATA` command:
We have removed the **Historical Bytes** from the initial `GET_DATA` command:

> Issue: The application freezes during GPG initialization (command `GET_DATA` `0x6E`) when using a hybrid ECC key configuration.
>
Expand Down
65 changes: 65 additions & 0 deletions doc/admonitions.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
-- Pandoc Lua filter: render RST admonitions as visible colored framed
-- boxes in LaTeX/PDF output, and as styled <div> blocks in HTML output.
--
-- Without this filter, Pandoc emits the admonition title as a plain
-- paragraph followed by the body, which is visually indistinguishable
-- from regular text in the generated PDF.

local types = {
danger = { title = "DANGER", fg = "white", bg = "red!85!black" },
error = { title = "ERROR", fg = "white", bg = "red!85!black" },
warning = { title = "WARNING", fg = "black", bg = "orange!30" },
caution = { title = "CAUTION", fg = "black", bg = "orange!30" },
important = { title = "IMPORTANT", fg = "black", bg = "yellow!40" },
attention = { title = "ATTENTION", fg = "black", bg = "yellow!40" },
note = { title = "NOTE", fg = "black", bg = "blue!12" },
tip = { title = "TIP", fg = "black", bg = "green!18" },
hint = { title = "HINT", fg = "black", bg = "green!18" },
}

local function has_title_subdiv(blk)
return blk.t == "Div" and blk.classes and blk.classes:includes("title")
end

function Div(el)
for _, cls in ipairs(el.classes) do
local spec = types[cls]
if spec then
-- Drop the title sub-div that Pandoc auto-inserts for RST admonitions.
local body = {}
for _, blk in ipairs(el.content) do
if not has_title_subdiv(blk) then
table.insert(body, blk)
end
end

if FORMAT:match("latex") then
local open = pandoc.RawBlock(
"latex",
"\\begin{center}\\noindent\\fcolorbox{black}{" .. spec.bg ..
"}{\\begin{minipage}{0.95\\textwidth}\\vspace{2pt}" ..
"\\textcolor{" .. spec.fg .. "}{\\textbf{" .. spec.title ..
"}}\\par\\vspace{4pt}"
)
local close = pandoc.RawBlock(
"latex",
"\\end{minipage}}\\end{center}"
)
table.insert(body, 1, open)
table.insert(body, close)
return body
end

if FORMAT:match("html") then
local title = pandoc.RawBlock(
"html",
'<p class="admonition-title">' .. spec.title .. "</p>"
)
table.insert(body, 1, title)
el.content = body
el.classes = { "admonition", cls }
return el
end
end
end
end
Binary file modified doc/developer/gpgcard-addon.pdf
Binary file not shown.
13 changes: 13 additions & 0 deletions doc/developer/gpgcard-addon.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ Key Backup

A full key backup mechanism is provided.

.. danger::
**On-device data is wiped on every App or OS update.**

The OpenPGP App stores all keys and metadata in its own non-volatile memory. Anything
generated or imported on the device is erased whenever the App is reinstalled, which
happens during any App update and during any OS update.

The backup/restore mechanism described here (and the companion ``pytools`` script) is,
today, the only available mitigation. It is **not trivial to operate and not guaranteed
to work in every scenario** — private-key restore in particular only works when
**SEED mode** was enabled when the backup was taken. It remains a procedure reserved
to advanced users.


Ledger OpenPGP Application
==========================
Expand Down
1 change: 1 addition & 0 deletions doc/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ OPTIONS=(
"--toc"
"--number-sections"
"--template=template.latex"
"--lua-filter=${SCRIPT_DIR}/admonitions.lua"
)

for name in "${NAMES[@]}"; do
Expand Down
1 change: 1 addition & 0 deletions doc/generate_html.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ COMMON_OPTIONS=(
"--to=html5"
"--css=${CSS}"
"--metadata=lang:en"
"--lua-filter=${SCRIPT_DIR}/admonitions.lua"
)

echo "Generating app-openpgp.html..."
Expand Down
64 changes: 64 additions & 0 deletions doc/html/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,67 @@ hr {
border-top: 1px solid #ddd;
margin: 2rem 0;
}

.admonition {
border: 1px solid #ccc;
border-left-width: 6px;
padding: 0.75rem 1rem;
margin: 1.25rem 0;
border-radius: 4px;
background: #f9f9f9;
}

.admonition .admonition-title {
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
margin: 0 0 0.5rem 0;
}

.admonition.danger,
.admonition.error {
border-color: #c0392b;
background: #fdecea;
}
.admonition.danger .admonition-title,
.admonition.error .admonition-title {
color: #c0392b;
}

.admonition.warning,
.admonition.caution {
border-color: #d68910;
background: #fdf2e3;
}
.admonition.warning .admonition-title,
.admonition.caution .admonition-title {
color: #b9770e;
}

.admonition.important,
.admonition.attention {
border-color: #b7950b;
background: #fcf6d8;
}
.admonition.important .admonition-title,
.admonition.attention .admonition-title {
color: #9a7d0a;
}

.admonition.note {
border-color: #2874a6;
background: #eaf2f8;
}
.admonition.note .admonition-title {
color: #2874a6;
}

.admonition.tip,
.admonition.hint {
border-color: #1e8449;
background: #e8f5ec;
}
.admonition.tip .admonition-title,
.admonition.hint .admonition-title {
color: #1e8449;
}
Binary file modified doc/user/app-openpgp.pdf
Binary file not shown.
36 changes: 36 additions & 0 deletions doc/user/app-openpgp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,31 @@ This release has known missing parts (see also [GPGADD]_):
- Seed mode ON/OFF via apdu


.. danger::
**On-device data is wiped on every App or OS update.**

All keys and metadata stored on the device — private keys, fingerprints, key templates,
cardholder data, PIN counters, etc. — are **erased** whenever the OpenPGP App is reinstalled.
This happens in two common situations:

- when the App itself is updated to a new version,
- when the device OS is updated, since the App is reinstalled as part of the OS update flow.

A backup/restore script is provided (see `Backup and Restore`_ and `Backup tool`_),
but it is **not trivial to operate and is not guaranteed to work in every scenario**.
In particular, private-key restore only works if **SEED mode** was enabled when the
backup was taken.

Until a permanent on-device storage mechanism is available, using the OpenPGP App in
production requires that you are comfortable with this constraint. Concretely:

- enable **SEED mode** before generating any key, so keys can be regenerated
deterministically,
- take a backup with the provided script **before every App or OS update**,
- never store on the device a key you cannot afford to lose or regenerate.

This limitation is structural and is not specific to a given release.


How to install GPG Application
==============================
Expand Down Expand Up @@ -1119,6 +1144,12 @@ Introduction
The OpenPGP card specification does not provide any mechanism for backuping you key.
Thus if you generate your keys on device and loose it, you definitively loose you private key.

.. danger::
The same data loss happens on **every App update and every OS update**: the OpenPGP App
is reinstalled in both cases, and all keys and metadata stored on the device are wiped.
You must take a backup (or rely on **SEED mode**) **before** any update, otherwise the
on-device material is gone for good.

In order to avoid such extreme panic situation, a backup/restore mechanism is provided.
At any time you can backup a snapshot of your device data, including your private keys.
All public data are retrieve in clear form. The private key are stored
Expand All @@ -1128,6 +1159,11 @@ The backup/restore tool is located in ``pytools`` directory.

See `Tools` later in this document for the tools details and usage.

.. warning::
This backup/restore script is **not trivial to operate and is not guaranteed to work
in every scenario**. It is, today, the only available mitigation against data loss on
App or OS update, and remains a procedure reserved to advanced users.

Note: The keys backup will work *only* if the SEED Mode is enabled!


Expand Down
Loading