Repository: paragonie/paseto (language: php)

Issue:
[Version 3] Cannot create token from encoded private key
A few months go I already reported #163. There are more problems with encoding a private key, and creating a token from the encoded private key. In order to show the problem, rather than reporting another issue, I created a test that shows the problem. I would not be surprised if #163 and this report are linked.

This code:
```php
$privateKey = AsymmetricSecretKey::generate(new Version3());
$privateKeyEncoded = $privateKey->encode();

(new Builder())
    ->setKey(AsymmetricSecretKey::fromEncodedString($privateKeyEncoded, new Version3()))
    ->setVersion(new Version3())
    ->setPurpose(Purpose::public())
    ->setIssuer('Test')
    ->toString();
```

Results in this error

```log
1) ParagonIE\Paseto\Tests\KeyTest::testBuildTokenWithReadingSecretKeyFromEncodedString
ParagonIE\Paseto\Exception\PasetoException: Signing failed.

/home/runner/work/paseto-1/paseto-1/src/Builder.php:668
/home/runner/work/paseto-1/paseto-1/tests/KeyTest.php:182

Caused by
FG\ASN1\Exception\ParserException: ASN.1 Parser Exception at offset 9: Can not parse content length (long form) from data: Offset index larger than input size

/home/runner/work/paseto-1/paseto-1/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php:331
/home/runner/work/paseto-1/paseto-1/vendor/fgrosse/phpasn1/lib/ASN1/ASNObject.php:270
/home/runner/work/paseto-1/paseto-1/vendor/mdanter/ecc/src/Serializer/PrivateKey/DerPrivateKeySerializer.php:91
/home/runner/work/paseto-1/paseto-1/vendor/mdanter/ecc/src/Serializer/PrivateKey/PemPrivateKeySerializer.php:54
/home/runner/work/paseto-1/paseto-1/vendor/paragonie/easy-ecc/src/ECDSA/SecretKey.php:60
/home/runner/work/paseto-1/paseto-1/src/Protocol/Version3.php:287
/home/runner/work/paseto-1/paseto-1/src/Builder.php:661
/home/runner/work/paseto-1/paseto-1/tests/KeyTest.php:182
```

If I encode the raw key myself, using simple base64 encoding (`base64_encode`), and then call `AsymmetricSecretKey::fromEncodedString()`, I am able to read the private key.


Discussion / hints:

Fix patch:
diff --git a/src/Keys/AsymmetricSecretKey.php b/src/Keys/AsymmetricSecretKey.php
index 2f6609a..5e9a629 100644
--- a/src/Keys/AsymmetricSecretKey.php
+++ b/src/Keys/AsymmetricSecretKey.php
@@ -237,11 +237,7 @@ public function encodePem(): string
     {
         switch ($this->protocol::header()) {
             case 'v3':
-                return Util::dos2unix((new SecretKey(
-                    new ConstantTimeMath(),
-                    EccFactory::getNistCurves()->generator384(),
-                    gmp_init(Hex::encode($this->raw()), 16)
-                ))->exportPem());
+                return $this->key;
             case 'v4':
                 $encoded = Base64::encode(
                     Hex::decode('302e020100300506032b657004220420') . $this->raw()
@@ -267,6 +263,18 @@ public function encodePem(): string
     public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self
     {
         $decoded = Base64UrlSafe::decodeNoPadding($encoded);
+
+        if ($version && hash_equals($version::header(), Version3::HEADER) && Binary::safeStrlen($decoded) === 48) {
+            return new self(
+                (new SecretKey(
+                    new ConstantTimeMath(),
+                    EccFactory::getNistCurves()->generator384(),
+                    \gmp_init(Hex::encode($decoded), 16)
+                ))->exportPem(),
+                $version
+            );
+        }
+
         return new self($decoded, $version);
     }
 


Test patch:
diff --git a/tests/KeyTest.php b/tests/KeyTest.php
index 92f24e5..95d73b1 100644
--- a/tests/KeyTest.php
+++ b/tests/KeyTest.php
@@ -7,6 +7,9 @@
     AsymmetricSecretKey
 };
 use ParagonIE\ConstantTime\Binary;
+use ParagonIE\EasyECC\ECDSA\SecretKey;
+use ParagonIE\Paseto\Builder;
+use ParagonIE\Paseto\Purpose;
 use ParagonIE\Paseto\Exception\{
     PasetoException,
     SecurityException,
@@ -165,4 +168,19 @@ public function testLongV4SymmetricKey()
         $this->expectException(PasetoException::class);
         new SymmetricKey(random_bytes(33), new Version4());
     }
+
+    public function testVersion3BuildTokenWithReadingSecretKeyFromEncodedString()
+    {
+        $privateKey = AsymmetricSecretKey::generate(new Version3());
+        $privateKeyEncoded = $privateKey->encode();
+
+        (new Builder())
+            ->setKey(AsymmetricSecretKey::fromEncodedString($privateKeyEncoded, new Version3()))
+            ->setVersion(new Version3())
+            ->setPurpose(Purpose::public())
+            ->setIssuer('Test')
+            ->toString();
+
+        $this->addToAssertionCount(1);
+    }
 }