1<?php
2
3/**
4 * libsodium Key Handler
5 *
6 * Different NaCl implementations store the key differently.
7 * https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ elaborates.
8 * libsodium appears to use the same format as SUPERCOP.
9 *
10 * PHP version 5
11 *
12 * @category  Crypt
13 * @package   EC
14 * @author    Jim Wigginton <terrafrost@php.net>
15 * @copyright 2015 Jim Wigginton
16 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
17 * @link      http://phpseclib.sourceforge.net
18 */
19
20namespace phpseclib3\Crypt\EC\Formats\Keys;
21
22use phpseclib3\Crypt\EC\Curves\Ed25519;
23use phpseclib3\Exception\UnsupportedFormatException;
24use phpseclib3\Math\BigInteger;
25
26/**
27 * libsodium Key Handler
28 *
29 * @package EC
30 * @author  Jim Wigginton <terrafrost@php.net>
31 * @access  public
32 */
33abstract class libsodium
34{
35    use Common;
36
37    /**
38     * Is invisible flag
39     *
40     * @access private
41     */
42    const IS_INVISIBLE = true;
43
44    /**
45     * Break a public or private key down into its constituent components
46     *
47     * @access public
48     * @param string $key
49     * @param string $password optional
50     * @return array
51     */
52    public static function load($key, $password = '')
53    {
54        switch (strlen($key)) {
55            case 32:
56                $public = $key;
57                break;
58            case 64:
59                $private = substr($key, 0, 32);
60                $public = substr($key, -32);
61                break;
62            case 96:
63                $public = substr($key, -32);
64                if (substr($key, 32, 32) != $public) {
65                    throw new \RuntimeException('Keys with 96 bytes should have the 2nd and 3rd set of 32 bytes match');
66                }
67                $private = substr($key, 0, 32);
68                break;
69            default:
70                throw new \RuntimeException('libsodium keys need to either be 32 bytes long, 64 bytes long or 96 bytes long');
71        }
72
73        $curve = new Ed25519();
74        $components = ['curve' => $curve];
75        if (isset($private)) {
76            $components['dA'] = $curve->extractSecret($private);
77        }
78        $components['QA'] = isset($public) ?
79            self::extractPoint($public, $curve) :
80            $curve->multiplyPoint($curve->getBasePoint(), $components['dA']);
81
82        return $components;
83    }
84
85    /**
86     * Convert an EC public key to the appropriate format
87     *
88     * @access public
89     * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
90     * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
91     * @return string
92     */
93    public static function savePublicKey(Ed25519 $curve, array $publicKey)
94    {
95        return $curve->encodePoint($publicKey);
96    }
97
98    /**
99     * Convert a private key to the appropriate format.
100     *
101     * @access public
102     * @param \phpseclib3\Math\BigInteger $privateKey
103     * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
104     * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
105     * @param string $password optional
106     * @return string
107     */
108    public static function savePrivateKey(BigInteger $privateKey, Ed25519 $curve, array $publicKey, $password = '')
109    {
110        if (!isset($privateKey->secret)) {
111            throw new \RuntimeException('Private Key does not have a secret set');
112        }
113        if (strlen($privateKey->secret) != 32) {
114            throw new \RuntimeException('Private Key secret is not of the correct length');
115        }
116        if (!empty($password) && is_string($password)) {
117            throw new UnsupportedFormatException('libsodium private keys do not support encryption');
118        }
119        return $privateKey->secret . $curve->encodePoint($publicKey);
120    }
121}
122