1010 * @author Joas Schilling <coding@schilljs.com>
1111 * @author Lukas Reschke <lukas@statuscode.ch>
1212 * @author Roeland Jago Douma <roeland@famdouma.nl>
13+ * @author Maxence Lange <maxence@artificial-owl.com>
1314 *
1415 * @license GNU AGPL version 3 or any later version
1516 *
2728 * along with this program. If not, see <http://www.gnu.org/licenses/>.
2829 *
2930 */
31+
3032namespace OC \Security \IdentityProof ;
3133
3234use OC \Files \AppData \Factory ;
35+ use OC \Security \IdentityProof \Exception \IdentityProofKeySumException ;
3336use OCP \Files \IAppData ;
37+ use OCP \Files \NotFoundException ;
38+ use OCP \Files \NotPermittedException ;
3439use OCP \IConfig ;
3540use OCP \IUser ;
41+ use OCP \PreConditionNotMetException ;
3642use OCP \Security \ICrypto ;
3743use Psr \Log \LoggerInterface ;
44+ use RuntimeException ;
3845
3946class Manager {
47+ public const SUM_PREFERENCES_KEY = 'identity_proof_key_sum ' ;
48+
4049 /** @var IAppData */
4150 private $ appData ;
4251 /** @var ICrypto */
@@ -61,7 +70,7 @@ public function __construct(Factory $appDataFactory,
6170 * In a separate function for unit testing purposes.
6271 *
6372 * @return array [$publicKey, $privateKey]
64- * @throws \ RuntimeException
73+ * @throws RuntimeException
6574 */
6675 protected function generateKeyPair (): array {
6776 $ config = [
@@ -74,12 +83,12 @@ protected function generateKeyPair(): array {
7483
7584 if ($ res === false ) {
7685 $ this ->logOpensslError ();
77- throw new \ RuntimeException ('OpenSSL reported a problem ' );
86+ throw new RuntimeException ('OpenSSL reported a problem ' );
7887 }
7988
8089 if (openssl_pkey_export ($ res , $ privateKey , null , $ config ) === false ) {
8190 $ this ->logOpensslError ();
82- throw new \ RuntimeException ('OpenSSL reported a problem ' );
91+ throw new RuntimeException ('OpenSSL reported a problem ' );
8392 }
8493
8594 // Extract the public key from $res to $pubKey
@@ -94,8 +103,10 @@ protected function generateKeyPair(): array {
94103 * Note: If a key already exists it will be overwritten
95104 *
96105 * @param string $id key id
106+ *
97107 * @return Key
98- * @throws \RuntimeException
108+ * @throws NotFoundException
109+ * @throws NotPermittedException
99110 */
100111 protected function generateKey (string $ id ): Key {
101112 [$ publicKey , $ privateKey ] = $ this ->generateKeyPair ();
@@ -109,54 +120,113 @@ protected function generateKey(string $id): Key {
109120 $ folder ->newFile ('private ' )
110121 ->putContent ($ this ->crypto ->encrypt ($ privateKey ));
111122 $ folder ->newFile ('public ' )
112- ->putContent ($ publicKey );
123+ ->putContent ($ publicKey );
113124
114125 return new Key ($ publicKey , $ privateKey );
115126 }
116127
117128 /**
118129 * Get key for a specific id
130+ * if $userId is set, a checksum of the key will be stored for future comparison
119131 *
120132 * @param string $id
133+ * @param string $userId
134+ *
121135 * @return Key
122- * @throws \RuntimeException
136+ * @throws NotFoundException
137+ * @throws NotPermittedException
138+ * @throws PreConditionNotMetException
123139 */
124- protected function retrieveKey (string $ id ): Key {
140+ protected function retrieveKey (string $ id, string $ userId = '' ): Key {
125141 try {
126142 $ folder = $ this ->appData ->getFolder ($ id );
127143 $ privateKey = $ this ->crypto ->decrypt (
128144 $ folder ->getFile ('private ' )->getContent ()
129145 );
130146 $ publicKey = $ folder ->getFile ('public ' )->getContent ();
131- return new Key ($ publicKey , $ privateKey );
147+ $ key = new Key ($ publicKey , $ privateKey );
148+
149+ $ this ->confirmSum ($ key , $ userId );
132150 } catch (\Exception $ e ) {
133- return $ this ->generateKey ($ id );
151+ $ key = $ this ->generateKey ($ id );
152+ $ this ->generateKeySum ($ key , $ userId );
134153 }
154+
155+ return $ key ;
135156 }
136157
158+
159+ /**
160+ * @param Key $key
161+ * @param string $userId
162+ *
163+ * @throws IdentityProofKeySumException
164+ * @throws PreConditionNotMetException
165+ */
166+ protected function confirmSum (Key $ key , string $ userId ): void {
167+ if ($ userId === '' ) {
168+ $ knownSum = $ this ->config ->getAppValue ('core ' , self ::SUM_PREFERENCES_KEY , '' );
169+ } else {
170+ $ knownSum = $ this ->config ->getUserValue ($ userId , 'core ' , self ::SUM_PREFERENCES_KEY , '' );
171+ }
172+
173+ if ($ knownSum === '' ) { // sum is not known, generate a new one
174+ $ this ->generateKeySum ($ key , $ userId );
175+ }
176+
177+ if ($ knownSum !== $ key ->getSum ()) {
178+ throw new IdentityProofKeySumException ();
179+ }
180+ }
181+
182+
183+ /**
184+ * @param Key $key
185+ * @param string $userId
186+ *
187+ * @return void
188+ * @throws PreConditionNotMetException
189+ */
190+ public function generateKeySum (Key $ key , string $ userId ): void {
191+ if ($ userId === '' ) {
192+ $ this ->config ->setAppValue ('core ' , self ::SUM_PREFERENCES_KEY , $ key ->getSum ());
193+ } else {
194+ $ this ->config ->setUserValue ($ userId , 'core ' , self ::SUM_PREFERENCES_KEY , $ key ->getSum ());
195+ }
196+
197+ }
198+
199+
137200 /**
138201 * Get public and private key for $user
139202 *
140203 * @param IUser $user
204+ *
141205 * @return Key
142- * @throws \RuntimeException
206+ * @throws NotFoundException
207+ * @throws NotPermittedException
208+ * @throws PreConditionNotMetException
143209 */
144210 public function getKey (IUser $ user ): Key {
145211 $ uid = $ user ->getUID ();
146- return $ this ->retrieveKey ('user- ' . $ uid );
212+
213+ return $ this ->retrieveKey ('user- ' . $ uid , $ uid );
147214 }
148215
149216 /**
150217 * Get instance wide public and private key
151218 *
152219 * @return Key
153- * @throws \RuntimeException
220+ * @throws NotFoundException
221+ * @throws NotPermittedException
222+ * @throws PreConditionNotMetException
154223 */
155224 public function getSystemKey (): Key {
156225 $ instanceId = $ this ->config ->getSystemValue ('instanceid ' , null );
157226 if ($ instanceId === null ) {
158- throw new \ RuntimeException ('no instance id! ' );
227+ throw new RuntimeException ('no instance id! ' );
159228 }
229+
160230 return $ this ->retrieveKey ('system- ' . $ instanceId );
161231 }
162232
0 commit comments