/ scripts / check-pgp-expiry.sh
check-pgp-expiry.sh
  1  #!/bin/bash
  2  
  3  # Validates PGP keys in the scripts/keys directory by checking their expiration
  4  # status and signing capabilities. It iterates through all .asc files and uses
  5  # GPG to parse key information.
  6  
  7  set -euo pipefail
  8  shopt -s nullglob
  9  
 10  error() {
 11      RED='\033[0;31m'
 12      NC='\033[0m' # No Color
 13      echo -e "${RED}ERROR: $1${NC}"
 14      exit_code=1
 15  }
 16  
 17  # Check if a key has expired or is expiring soon
 18  # Args: $1 = expiry timestamp, $2 = key_info
 19  # Returns: 0 if key is valid or has no expiry, 1 if expired/expiring soon
 20  check_key_expiry() {
 21      local expiry="$1"
 22      local key_info="$2"
 23  
 24      # If expiry is empty, the key does not expire.
 25      if [[ -z "$expiry" ]]; then
 26          echo "INFO: $key_info does not expire"
 27          return 0
 28      fi
 29  
 30      # Convert expiry timestamp to human readable date for logging.
 31      local expiry_date
 32      if ! expiry_date=$(date -d "@$expiry" "+%Y-%m-%d" 2>/dev/null \
 33          || date -r "$expiry" "+%Y-%m-%d" 2>/dev/null); then
 34          error "Invalid expiry timestamp for $key_info $expiry"
 35          return 1
 36      fi
 37  
 38      if (( expiry < $(date +%s) )); then
 39          echo "WARN: $key_info has already expired ($expiry_date)"
 40          return 1
 41      fi
 42  
 43      if (( expiry < EXPIRE_THRESHOLD )); then
 44          echo "WARN: $key_info expires soon ($expiry_date)"
 45          return 1
 46      fi
 47  
 48      echo "INFO: $key_info is valid until $expiry_date"
 49      return 0
 50  }
 51  
 52  echo
 53  echo "Starting PGP key validation..."
 54  
 55  KEY_DIR="./scripts/keys"
 56  if [[ ! -d "$KEY_DIR" ]]; then
 57      error "Directory $KEY_DIR does not exist"
 58      exit $exit_code
 59  fi
 60  
 61  key_files=("$KEY_DIR"/*.asc)
 62  if (( ${#key_files[@]} == 0 )); then
 63      error "No PGP keys found in $KEY_DIR"
 64      exit $exit_code
 65  fi
 66  
 67  # 2 weeks = 14 days * (24 hours * 60 minutes * 60 seconds).
 68  EXPIRE_THRESHOLD=$(($(date +%s) + 14 * 86400))
 69  exit_code=0
 70  
 71  echo "Found ${#key_files[@]} key file(s) in $KEY_DIR"
 72  echo
 73  
 74  for key_file in "${key_files[@]}"; do
 75      echo "────────────────────────────────────────────────────────────────────"
 76      echo "Checking $(basename "$key_file")..."
 77  
 78      gpg_output=$(gpg --with-colons --import-options show-only \
 79        --import "$key_file" 2>&1 | grep -E '^(pub|sub):')
 80  
 81      key_name=$(basename "$key_file")
 82  
 83      # Parse GPG output line by line to find key type, id, expiry and
 84      # capabilities.
 85      valid_sign_key_found=false
 86      while IFS=: read -r type _ _ _ id _ expiry _ _ _ _ capabilities _; do
 87          if [[ "$valid_sign_key_found" == true ]]; then
 88              # If we already found a valid signing key, skip further checks for
 89              # this keychain.
 90              break
 91          fi
 92  
 93          key_info="$type:$id ($capabilities)"
 94  
 95          # Check primary key expiry.
 96          if [[ "$type" == "pub" ]] &&
 97              ! check_key_expiry "$expiry" "$key_info"; then
 98              error "$key_info primary key is invalid"
 99              break
100          fi
101  
102          # Filter out keys that cannot sign releases.
103          if ! [[ "$capabilities" =~ [sS] ]]; then
104              continue
105          fi
106  
107          # Check sub key expiry.
108          if [[ "$type" == "sub" ]] &&
109              ! check_key_expiry "$expiry" "$key_info"; then
110              continue
111          fi
112  
113          # If we reach here, we have a valid signing key.
114          valid_sign_key_found=true
115      done <<< "$gpg_output"
116  
117      if [[ "$valid_sign_key_found" == false ]]; then
118          error "$key_name does not have any valid sign key"
119      fi
120  done
121  
122  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
123  if [[ $exit_code -eq 0 ]]; then
124      echo "All PGP keys are valid and ready for use!"
125  else
126      error "Some PGP keys have issues that need attention."
127  fi
128  
129  exit $exit_code