Pinentry: Difference between revisions
(Created page with " echo -e 'SETTITLE YUBI\nSETDESC YUBI\nSETPROMPT YUBI\nSETERROR YUBI\nGETPIN\n' | /usr/bin/pinentry | grep '^D' sample-pinentry.sh :: <pre> #!/bin/bash # Ensure pinentry is installed command -v pinentry >/dev/null 2>&1 || { echo "ERROR: pinentry not found" exit 1 } # Talk to pinentry using its text protocol response=$( { echo "SETTITLE Credential Prompt" echo "SETPROMPT Passphrase:" echo "SETDESC Please enter your passphrase." echo "GETPIN" }...") |
|||
| Line 195: | Line 195: | ||
echo "Got password (${#secret} chars)" | echo "Got password (${#secret} chars)" | ||
</pre> | </pre> | ||
== Normal Usage == | |||
echo "hello" > test.txt | |||
gpg -c test.txt | |||
will trigger: | |||
<pre> | |||
gpg | |||
-> gpg-agent | |||
-> pinentry | |||
</pre> | |||
== reusable password prompt via gpg-agent == | |||
gpg-preset-passphrase | |||
# or | |||
gpg-agent --daemon | |||
== Quick and dirty == | |||
<pre> | |||
{ | |||
echo "SETTITLE Login" | |||
echo "SETPROMPT Password:" | |||
echo "GETPIN" | |||
} | pinentry | |||
</pre> | |||
<pre> | |||
#!/bin/bash | |||
output=$( | |||
{ | |||
echo "SETTITLE Test" | |||
echo "SETPROMPT Password:" | |||
echo "GETPIN" | |||
} | pinentry | |||
) | |||
echo "----- RAW OUTPUT -----" | |||
printf '%s\n' "$output" | |||
echo "----------------------" | |||
</pre> | |||
if "D secret", then: | |||
<pre> | |||
secret=$(printf '%s\n' "$output" | sed -n 's/^D //p') | |||
</pre> | |||
If it contains percent-encoding: "D abc%20def", then you will need to decode before use | |||
== Another Try == | |||
<pre> | |||
export GPG_TTY=$(tty) | |||
gpg-connect-agent updatestartuptty /bye | |||
gpg-connect-agent <<EOF | |||
GET_PASSPHRASE mytest X X "Enter Password" | |||
EOF | |||
</pre> | |||
Decode with: | |||
xxd -r -p | |||
<pre> | |||
{ | |||
gpg-connect-agent <<EOF | |||
GET_PASSPHRASE mytest X X "Enter Password" | |||
EOF | |||
} | xxd -r -p | |||
</pre> | |||
# echo "pinentry-program /usr/bin/pinentry-tty" >> ~/.gnupg/gpg-agent.conf | |||
echo "pinentry-program /usr/bin/pinentry-gtk" >> ~/.gnupg/gpg-agent.conf | |||
gpgconf --kill gpg-agent | |||
--- | |||
~/.gnupg/gpg-agent.conf | |||
<pre> | |||
# pinentry-tty renders cleanly in the Copilot CLI terminal (no curses/mouse junk) | |||
pinentry-program /usr/bin/pinentry-tty | |||
# Allow non-interactive passphrase paths | |||
allow-loopback-pinentry | |||
allow-preset-passphrase | |||
# Keep the passphrase cached for a full workday so signing is silent after one unlock | |||
default-cache-ttl 34200 | |||
max-cache-ttl 34200 | |||
</pre> | |||
== another try == | |||
Fix #3: Use a dedicated TTY | |||
A trick for scripts is to communicate over stdin/stdout while pinentry accesses /dev/tty: | |||
<pre> | |||
#!/bin/bash | |||
exec 3<> /dev/tty | |||
output=$( | |||
{ | |||
echo "SETTITLE Login" | |||
echo "SETPROMPT Password:" | |||
echo "GETPIN" | |||
} | pinentry 2>&1 | |||
) | |||
echo "$output" | |||
</pre> | |||
--- | |||
<pre> | |||
gpg-connect-agent <<EOF | |||
GET_PASSPHRASE mytest X X "Test" | |||
EOF | |||
</pre> | |||
--- | |||
<pre> | |||
{ | |||
echo "GETPIN" | |||
} | pinentry | |||
</pre> | |||
--- | |||
<pre> | |||
export GPG_TTY=$(tty) | |||
pinentry-curses <<EOF | |||
SETTITLE Login | |||
SETPROMPT Password: | |||
GETPIN | |||
EOF | |||
</pre> | |||
== decode version == | |||
<pre> | |||
#!/bin/bash | |||
response=$( | |||
gpg-connect-agent <<EOF | |||
GET_PASSPHRASE mytest X X "Enter Password" | |||
EOF | |||
) | |||
hex=$(awk '/^OK / {print $2}' <<< "$response") | |||
password=$(printf '%s' "$hex" | xxd -r -p) | |||
echo "Password: $password" | |||
</pre> | |||
== keywords == | |||
Revision as of 21:50, 26 August 2026
echo -e 'SETTITLE YUBI\nSETDESC YUBI\nSETPROMPT YUBI\nSETERROR YUBI\nGETPIN\n' | /usr/bin/pinentry | grep '^D'
sample-pinentry.sh ::
#!/bin/bash
# Ensure pinentry is installed
command -v pinentry >/dev/null 2>&1 || {
echo "ERROR: pinentry not found"
exit 1
}
# Talk to pinentry using its text protocol
response=$(
{
echo "SETTITLE Credential Prompt"
echo "SETPROMPT Passphrase:"
echo "SETDESC Please enter your passphrase."
echo "GETPIN"
} | pinentry
)
# Extract the entered PIN/password
pin=$(echo "$response" | awk '/^D / {print substr($0,3)}')
if [[ -n "$pin" ]]; then
echo "Passphrase was entered."
echo "Length: ${#pin}"
# Do something with $pin here
else
echo "No passphrase entered or dialog cancelled."
fi
secure-sample-pinentry.sh ::
#!/bin/bash
pin=$(
{
echo "SETTITLE Secure Prompt"
echo "SETPROMPT Password:"
echo "GETPIN"
} | pinentry | sed -n 's/^D //p'
)
if [[ -z "$pin" ]]; then
echo "Cancelled"
exit 1
fi
# Use the secret
echo "Received secret"
# Clear the variable when done
unset pin
Info
When the user enters a value, pinentry returns lines similar to:
OK Pleased to meet you D MySecretPassword OK
The line beginning with D contains the entered secret, which is why the script extracts it.
gpg-agent version
Example 1: Ask for a passphrase via gpg-agent
#!/bin/bash
# Establish a connection to gpg-agent
GPG_CONNECT=$(gpg-connect-agent /bye 2>/dev/null)
if [[ $? -ne 0 ]]; then
echo "gpg-agent is not running"
exit 1
fi
# Request a passphrase
response=$(
gpg-connect-agent <<EOF
GET_PASSPHRASE --data mycacheid X X "Enter your password"
/bye
EOF
)
password=$(echo "$response" | sed -n 's/^D //p')
if [[ -z "$password" ]]; then
echo "User cancelled"
exit 1
fi
echo "Password received"
unset password
This caches the password, so if you call it again, it will have it stored.
--
Example 2: Trigger the user's configured pinentry
This example lets gpg-agent display the configured Pinentry GUI:
#!/bin/bash
result=$(
gpg-connect-agent <<'EOF'
GET_PASSPHRASE myapp-login X X "Please enter your password"
/bye
EOF
)
secret=$(echo "$result" | sed -n 's/^D //p')
if [[ -n "$secret" ]]; then
echo "Success"
else
echo "Cancelled"
fi
--
Example 3: Cache the secret
A nice feature of gpg-agent is passphrase caching:
#!/bin/bash CACHE_ID="my-company-tool" secret=$( gpg-connect-agent <<EOF | GET_PASSPHRASE $CACHE_ID X X "Authentication Required" /bye EOF sed -n 's/^D //p' ) echo "Secret obtained"
This one didn't seem to work for me?
--- Setup --
export GPG_TTY=$(tty) gpg-connect-agent updatestartuptty /bye
Configure ~/.gnupg/gpg-agent.conf
pinentry-program /usr/bin/pinentry-tty pinentry-program /usr/bin/pinentry-curses
--- manually test --
gpg-connect-agent <<EOF GET_PASSPHRASE mytest X X "Enter Password" EOF
Direct Pin Entry Example
#!/bin/bash
output=$(
{
echo "SETTITLE Login"
echo "SETPROMPT Password:"
echo "SETDESC Please enter your password."
echo "GETPIN"
} | pinentry
)
secret=$(sed -n 's/^D //p' <<< "$output")
if [[ -z "$secret" ]]; then
echo "Cancelled"
exit 1
fi
echo "Got password (${#secret} chars)"
Normal Usage
echo "hello" > test.txt gpg -c test.txt
will trigger:
gpg
-> gpg-agent
-> pinentry
reusable password prompt via gpg-agent
gpg-preset-passphrase # or gpg-agent --daemon
Quick and dirty
{
echo "SETTITLE Login"
echo "SETPROMPT Password:"
echo "GETPIN"
} | pinentry
#!/bin/bash
output=$(
{
echo "SETTITLE Test"
echo "SETPROMPT Password:"
echo "GETPIN"
} | pinentry
)
echo "----- RAW OUTPUT -----"
printf '%s\n' "$output"
echo "----------------------"
if "D secret", then:
secret=$(printf '%s\n' "$output" | sed -n 's/^D //p')
If it contains percent-encoding: "D abc%20def", then you will need to decode before use
Another Try
export GPG_TTY=$(tty) gpg-connect-agent updatestartuptty /bye gpg-connect-agent <<EOF GET_PASSPHRASE mytest X X "Enter Password" EOF
Decode with:
xxd -r -p
{
gpg-connect-agent <<EOF
GET_PASSPHRASE mytest X X "Enter Password"
EOF
} | xxd -r -p
# echo "pinentry-program /usr/bin/pinentry-tty" >> ~/.gnupg/gpg-agent.conf echo "pinentry-program /usr/bin/pinentry-gtk" >> ~/.gnupg/gpg-agent.conf gpgconf --kill gpg-agent
---
~/.gnupg/gpg-agent.conf
# pinentry-tty renders cleanly in the Copilot CLI terminal (no curses/mouse junk) pinentry-program /usr/bin/pinentry-tty # Allow non-interactive passphrase paths allow-loopback-pinentry allow-preset-passphrase # Keep the passphrase cached for a full workday so signing is silent after one unlock default-cache-ttl 34200 max-cache-ttl 34200
another try
Fix #3: Use a dedicated TTY
A trick for scripts is to communicate over stdin/stdout while pinentry accesses /dev/tty:
#!/bin/bash
exec 3<> /dev/tty
output=$(
{
echo "SETTITLE Login"
echo "SETPROMPT Password:"
echo "GETPIN"
} | pinentry 2>&1
)
echo "$output"
---
gpg-connect-agent <<EOF GET_PASSPHRASE mytest X X "Test" EOF
---
{
echo "GETPIN"
} | pinentry
---
export GPG_TTY=$(tty) pinentry-curses <<EOF SETTITLE Login SETPROMPT Password: GETPIN EOF
decode version
#!/bin/bash
response=$(
gpg-connect-agent <<EOF
GET_PASSPHRASE mytest X X "Enter Password"
EOF
)
hex=$(awk '/^OK / {print $2}' <<< "$response")
password=$(printf '%s' "$hex" | xxd -r -p)
echo "Password: $password"