]> git.basschouten.com Git - openhab-addons.git/blob
57904c2737232ff45a6fe3b240eae1c1263fb7fe
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.io.homekit.internal;
14
15 import java.math.BigInteger;
16 import java.security.InvalidAlgorithmParameterException;
17 import java.util.Base64;
18 import java.util.Collection;
19 import java.util.HashSet;
20 import java.util.stream.Collectors;
21
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.core.storage.Storage;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import io.github.hapjava.server.HomekitAuthInfo;
28 import io.github.hapjava.server.impl.HomekitServer;
29
30 /**
31  * Provides a mechanism to store authenticated HomeKit client details inside the
32  * StorageService, by implementing HomekitAuthInfo.
33  *
34  * @author Andy Lintner - Initial contribution
35  */
36 public class HomekitAuthInfoImpl implements HomekitAuthInfo {
37     private final Logger logger = LoggerFactory.getLogger(HomekitAuthInfoImpl.class);
38     public static final String STORAGE_KEY = "homekit";
39     private static final String STORAGE_MAC = "mac";
40     private static final String STORAGE_SALT = "salt";
41     private static final String STORAGE_PRIVATE_KEY = "privateKey";
42     private static final String STORAGE_USER_PREFIX = "user_";
43
44     private final Storage<Object> storage;
45     private String mac;
46     private BigInteger salt;
47     private byte[] privateKey;
48     private String pin;
49     private String setupId;
50     private boolean blockUserDeletion;
51
52     public HomekitAuthInfoImpl(Storage<Object> storage, String pin, String setupId, boolean blockUserDeletion)
53             throws InvalidAlgorithmParameterException {
54         this.storage = storage;
55         this.pin = pin;
56         this.setupId = setupId;
57         this.blockUserDeletion = blockUserDeletion;
58         initializeStorage();
59     }
60
61     public void setBlockUserDeletion(boolean blockUserDeletion) {
62         this.blockUserDeletion = blockUserDeletion;
63     }
64
65     @Override
66     public void createUser(String username, byte[] publicKey, boolean isAdmin) {
67         logger.trace("create user {}", username);
68         final String userKey = createUserKey(username);
69         final String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey);
70         storage.put(userKey, encodedPublicKey);
71         logger.trace("stored user key {} with value {}", userKey, encodedPublicKey);
72     }
73
74     @Override
75     public String getMac() {
76         return mac;
77     }
78
79     public void setMac(String mac) {
80         this.mac = mac;
81     }
82
83     @Override
84     public String getPin() {
85         return pin;
86     }
87
88     public void setPin(String pin) {
89         this.pin = pin;
90     }
91
92     @Override
93     public String getSetupId() {
94         return setupId;
95     }
96
97     public void setSetupId(String setupId) {
98         this.setupId = setupId;
99     }
100
101     @Override
102     public byte[] getPrivateKey() {
103         return privateKey;
104     }
105
106     @Override
107     public BigInteger getSalt() {
108         return salt;
109     }
110
111     @Override
112     public byte[] getUserPublicKey(String username) {
113         final String encodedKey = (String) storage.get(createUserKey(username));
114         if (encodedKey != null) {
115             return Base64.getDecoder().decode(encodedKey);
116         } else {
117             return null;
118         }
119     }
120
121     @Override
122     public void removeUser(String username) {
123         logger.trace("remove user {}", username);
124         if (!this.blockUserDeletion) {
125             storage.remove(createUserKey(username));
126         } else {
127             logger.debug("deletion of the user was blocked by binding settings");
128         }
129     }
130
131     @Override
132     public boolean hasUser() {
133         Collection<String> keys = storage.getKeys();
134         return keys.stream().anyMatch(this::isUserKey);
135     }
136
137     @Override
138     public Collection<String> listUsers() {
139         Collection<String> keys = storage.getKeys();
140         // don't forget to strip user_ prefix
141         return keys.stream().filter(this::isUserKey).map(u -> u.substring(5)).collect(Collectors.toList());
142     }
143
144     @Override
145     public boolean userIsAdmin(String username) {
146         return true;
147     }
148
149     public void clear() {
150         logger.trace("clear all users");
151         if (!this.blockUserDeletion) {
152             for (String key : new HashSet<>(storage.getKeys())) {
153                 if (isUserKey(key)) {
154                     storage.remove(key);
155                 }
156             }
157         } else {
158             logger.debug("deletion of users information was blocked by binding settings");
159         }
160     }
161
162     private String createUserKey(String username) {
163         return STORAGE_USER_PREFIX + username;
164     }
165
166     private boolean isUserKey(String key) {
167         return key.startsWith(STORAGE_USER_PREFIX);
168     }
169
170     private void initializeStorage() throws InvalidAlgorithmParameterException {
171         mac = (String) storage.get(STORAGE_MAC);
172         final @Nullable Object saltConfig = storage.get(STORAGE_SALT);
173         final @Nullable Object privateKeyConfig = storage.get(STORAGE_PRIVATE_KEY);
174         if (mac == null) {
175             logger.warn(
176                     "could not find existing MAC in {}. Generating new MAC. This will require re-pairing of iOS devices.",
177                     storage.getClass().getName());
178             mac = HomekitServer.generateMac();
179             storage.put(STORAGE_MAC, mac);
180         }
181         if (saltConfig == null) {
182             salt = HomekitServer.generateSalt();
183             storage.put(STORAGE_SALT, salt.toString());
184         } else {
185             salt = new BigInteger(saltConfig.toString());
186         }
187         if (privateKeyConfig == null) {
188             privateKey = HomekitServer.generateKey();
189             storage.put(STORAGE_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey));
190         } else {
191             privateKey = Base64.getDecoder().decode(privateKeyConfig.toString());
192         }
193     }
194 }