/**
  * Handles a status line.
  *
  * @param string $line the status line to handle.
  */
 public function handle($line)
 {
     $tokens = explode(' ', $line);
     switch ($tokens[0]) {
         case 'ENC_TO':
             // Now we know the message is encrypted. Set flag to check if
             // decryption succeeded.
             $this->decryptionOkay = false;
             // this is the new key message
             $this->currentSubKeyId = $tokens[1];
             break;
         case 'NEED_PASSPHRASE':
             // send passphrase to the GPG engine
             $subKeyId = $tokens[1];
             if (array_key_exists($subKeyId, $this->keys)) {
                 $passphrase = $this->keys[$subKeyId]['passphrase'];
                 $this->engine->sendCommand($passphrase);
             } else {
                 $this->engine->sendCommand('');
             }
             break;
         case 'USERID_HINT':
             // remember the user id for pretty exception messages
             $this->badPassphrases[$tokens[1]] = implode(' ', array_splice($tokens, 2));
             break;
         case 'GOOD_PASSPHRASE':
             // if we got a good passphrase, remove the key from the list of
             // bad passphrases.
             unset($this->badPassphrases[$this->currentSubKeyId]);
             break;
         case 'MISSING_PASSPHRASE':
             $this->missingPassphrases[$this->currentSubKeyId] = $this->currentSubKeyId;
             break;
         case 'NO_SECKEY':
             // note: this message is also received if there are multiple
             // recipients and a previous key had a correct passphrase.
             $this->missingKeys[$tokens[1]] = $tokens[1];
             break;
         case 'NODATA':
             $this->noData = true;
             break;
         case 'DECRYPTION_OKAY':
             // If the message is encrypted, this is the all-clear signal.
             $this->decryptionOkay = true;
             break;
     }
 }
Esempio n. 2
0
 /**
  * Handles error values in the status output from GPG
  *
  * This method is responsible for setting the
  * {@link self::$errorCode}. See <b>doc/DETAILS</b> in the
  * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
  * information on GPG's status output.
  *
  * @param string $line the status line to handle.
  *
  * @return void
  */
 public function handleStatus($line)
 {
     $tokens = explode(' ', $line);
     switch ($tokens[0]) {
         case 'NODATA':
             $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
             break;
         case 'DECRYPTION_OKAY':
             // If the message is encrypted, this is the all-clear signal.
             $this->data['DecryptionOkay'] = true;
             $this->errorCode = Crypt_GPG::ERROR_NONE;
             break;
         case 'DELETE_PROBLEM':
             if ($tokens[1] == '1') {
                 $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
                 break;
             } elseif ($tokens[1] == '2') {
                 $this->errorCode = Crypt_GPG::ERROR_DELETE_PRIVATE_KEY;
                 break;
             }
             break;
         case 'IMPORT_OK':
             $this->data['Import']['fingerprint'] = $tokens[2];
             if (empty($this->data['Import']['fingerprints'])) {
                 $this->data['Import']['fingerprints'] = array($tokens[2]);
             } else {
                 if (!in_array($tokens[2], $this->data['Import']['fingerprints'])) {
                     $this->data['Import']['fingerprints'][] = $tokens[2];
                 }
             }
             break;
         case 'IMPORT_RES':
             $this->data['Import']['public_imported'] = intval($tokens[3]);
             $this->data['Import']['public_unchanged'] = intval($tokens[5]);
             $this->data['Import']['private_imported'] = intval($tokens[11]);
             $this->data['Import']['private_unchanged'] = intval($tokens[12]);
             break;
         case 'NO_PUBKEY':
         case 'NO_SECKEY':
             $this->data['ErrorKeyId'] = $tokens[1];
             if ($this->errorCode != Crypt_GPG::ERROR_MISSING_PASSPHRASE && $this->errorCode != Crypt_GPG::ERROR_BAD_PASSPHRASE) {
                 $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
             }
             // note: this message is also received if there are multiple
             // recipients and a previous key had a correct passphrase.
             $this->data['MissingKeys'][$tokens[1]] = $tokens[1];
             // @FIXME: remove missing passphrase registered in ENC_TO handler
             //         This is for GnuPG 2.1
             unset($this->data['MissingPassphrases'][$tokens[1]]);
             break;
         case 'KEY_CONSIDERED':
             // In GnuPG 2.1.x exporting/importing a secret key requires passphrase
             // However, no NEED_PASSPRASE is returned, https://bugs.gnupg.org/gnupg/issue2667
             // So, handling KEY_CONSIDERED and GET_HIDDEN is needed.
             if (!array_key_exists('KeyConsidered', $this->data)) {
                 $this->data['KeyConsidered'] = $tokens[1];
             }
             break;
         case 'USERID_HINT':
             // remember the user id for pretty exception messages
             // GnuPG 2.1.15 gives me: "USERID_HINT 0000000000000000 [?]"
             $keyId = $tokens[1];
             if (strcspn($keyId, '0')) {
                 $username = implode(' ', array_splice($tokens, 2));
                 $this->data['BadPassphrases'][$keyId] = $username;
             }
             break;
         case 'ENC_TO':
             // Now we know the message is encrypted. Set flag to check if
             // decryption succeeded.
             $this->data['DecryptionOkay'] = false;
             // this is the new key message
             $this->data['CurrentSubKeyId'] = $keyId = $tokens[1];
             // For some reason in GnuPG 2.1.11 I get only ENC_TO and no
             // NEED_PASSPHRASE/MISSING_PASSPHRASE/USERID_HINT
             // This is not needed for GnuPG 2.1.15
             if (!empty($_ENV['PINENTRY_USER_DATA'])) {
                 $passphrases = json_decode($_ENV['PINENTRY_USER_DATA'], true);
             } else {
                 $passphrases = array();
             }
             // @TODO: Get user name/email
             $this->data['BadPassphrases'][$keyId] = $keyId;
             if (empty($passphrases) || empty($passphrases[$keyId])) {
                 $this->data['MissingPassphrases'][$keyId] = $keyId;
             }
             break;
         case 'GOOD_PASSPHRASE':
             // if we got a good passphrase, remove the key from the list of
             // bad passphrases.
             if (isset($this->data['CurrentSubKeyId'])) {
                 unset($this->data['BadPassphrases'][$this->data['CurrentSubKeyId']]);
                 unset($this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]);
             }
             $this->needPassphrase--;
             break;
         case 'BAD_PASSPHRASE':
             $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
             break;
         case 'MISSING_PASSPHRASE':
             if (isset($this->data['CurrentSubKeyId'])) {
                 $this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']] = $this->data['CurrentSubKeyId'];
             }
             $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
             break;
         case 'GET_HIDDEN':
             if ($tokens[1] == 'passphrase.enter' && isset($this->data['KeyConsidered'])) {
                 $tokens[1] = $this->data['KeyConsidered'];
             } else {
                 break;
             }
             // no break
         // no break
         case 'NEED_PASSPHRASE':
             $passphrase = $this->getPin($tokens[1]);
             $this->engine->sendCommand($passphrase);
             if ($passphrase === '') {
                 $this->needPassphrase++;
             }
             break;
         case 'SIG_CREATED':
             $this->data['SigCreated'] = $line;
             break;
         case 'SIG_ID':
             // note: signature id comes before new signature line and may not
             // exist for some signature types
             $this->data['SignatureId'] = $tokens[1];
             break;
         case 'EXPSIG':
         case 'EXPKEYSIG':
         case 'REVKEYSIG':
         case 'BADSIG':
         case 'ERRSIG':
             $this->errorCode = Crypt_GPG::ERROR_BAD_SIGNATURE;
             // no break
         // no break
         case 'GOODSIG':
             $signature = new Crypt_GPG_Signature();
             // if there was a signature id, set it on the new signature
             if (!empty($this->data['SignatureId'])) {
                 $signature->setId($this->data['SignatureId']);
                 $this->data['SignatureId'] = '';
             }
             // Detect whether fingerprint or key id was returned and set
             // signature values appropriately. Key ids are strings of either
             // 16 or 8 hexadecimal characters. Fingerprints are strings of 40
             // hexadecimal characters. The key id is the last 16 characters of
             // the key fingerprint.
             if (mb_strlen($tokens[1], '8bit') > 16) {
                 $signature->setKeyFingerprint($tokens[1]);
                 $signature->setKeyId(mb_substr($tokens[1], -16, null, '8bit'));
             } else {
                 $signature->setKeyId($tokens[1]);
             }
             // get user id string
             if ($tokens[0] != 'ERRSIG') {
                 $string = implode(' ', array_splice($tokens, 2));
                 $string = rawurldecode($string);
                 $signature->setUserId(Crypt_GPG_UserId::parse($string));
             }
             $this->data['Signatures'][] = $signature;
             break;
         case 'VALIDSIG':
             if (empty($this->data['Signatures'])) {
                 break;
             }
             $signature = end($this->data['Signatures']);
             $signature->setValid(true);
             $signature->setKeyFingerprint($tokens[1]);
             if (strpos($tokens[3], 'T') === false) {
                 $signature->setCreationDate($tokens[3]);
             } else {
                 $signature->setCreationDate(strtotime($tokens[3]));
             }
             if (array_key_exists(4, $tokens)) {
                 if (strpos($tokens[4], 'T') === false) {
                     $signature->setExpirationDate($tokens[4]);
                 } else {
                     $signature->setExpirationDate(strtotime($tokens[4]));
                 }
             }
             break;
         case 'KEY_CREATED':
             if (isset($this->data['Handle']) && $tokens[3] == $this->data['Handle']) {
                 $this->data['KeyCreated'] = $tokens[2];
             }
             break;
         case 'KEY_NOT_CREATED':
             if (isset($this->data['Handle']) && $tokens[1] == $this->data['Handle']) {
                 $this->errorCode = Crypt_GPG::ERROR_KEY_NOT_CREATED;
             }
             break;
         case 'PROGRESS':
             // todo: at some point, support reporting status async
             break;
             // GnuPG 2.1 uses FAILURE and ERROR responses
         // GnuPG 2.1 uses FAILURE and ERROR responses
         case 'FAILURE':
         case 'ERROR':
             $errnum = (int) $tokens[2];
             $source = $errnum >> 24;
             $errcode = $errnum & 0xffffff;
             switch ($errcode) {
                 case 11:
                     // bad passphrase
                 // bad passphrase
                 case 87:
                     // bad PIN
                     $this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
                     break;
                 case 177:
                     // no passphrase
                 // no passphrase
                 case 178:
                     // no PIN
                     $this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
                     break;
                 case 58:
                     $this->errorCode = Crypt_GPG::ERROR_NO_DATA;
                     break;
             }
             break;
     }
 }
Esempio n. 3
0
 /**
  * Handles the status output from GPG for the sign operation
  *
  * This method is responsible for sending the passphrase commands when
  * required by the {@link Crypt_GPG::sign()} method. See <b>doc/DETAILS</b>
  * in the {@link http://www.gnupg.org/download/ GPG distribution} for
  * detailed information on GPG's status output.
  *
  * @param string $line the status line to handle.
  *
  * @return void
  *
  * @see Crypt_GPG::sign()
  */
 public function handleSignStatus($line)
 {
     $tokens = explode(' ', $line);
     switch ($tokens[0]) {
         case 'NEED_PASSPHRASE':
             $subKeyId = $tokens[1];
             if (array_key_exists($subKeyId, $this->signKeys)) {
                 $passphrase = $this->signKeys[$subKeyId]['passphrase'];
                 $this->engine->sendCommand($passphrase);
             } else {
                 $this->engine->sendCommand('');
             }
             break;
         case 'SIG_CREATED':
             $this->lastSignatureInfo = $line;
             break;
     }
 }