]> git.basschouten.com Git - openhab-addons.git/blob
941081150f147015555b4a7dc646ab330d6be784
[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
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.core.storage.Storage;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 import io.github.hapjava.server.HomekitAuthInfo;
27 import io.github.hapjava.server.impl.HomekitServer;
28
29 /**
30  * Provides a mechanism to store authenticated HomeKit client details inside the
31  * StorageService, by implementing HomekitAuthInfo.
32  *
33  * @author Andy Lintner - Initial contribution
34  */
35 public class HomekitAuthInfoImpl implements HomekitAuthInfo {
36     private final Logger logger = LoggerFactory.getLogger(HomekitAuthInfoImpl.class);
37     public static final String STORAGE_KEY = "homekit";
38     private static final String STORAGE_MAC = "mac";
39     private static final String STORAGE_SALT = "salt";
40     private static final String STORAGE_PRIVATE_KEY = "privateKey";
41     private static final String STORAGE_USER_PREFIX = "user_";
42
43     private final Storage<Object> storage;
44     private String mac;
45     private BigInteger salt;
46     private byte[] privateKey;
47     private String pin;
48     private String setupId;
49     private boolean blockUserDeletion;
50
51     public HomekitAuthInfoImpl(Storage<Object> storage, String pin, String setupId, boolean blockUserDeletion)
52             throws InvalidAlgorithmParameterException {
53         this.storage = storage;
54         this.pin = pin;
55         this.setupId = setupId;
56         this.blockUserDeletion = blockUserDeletion;
57         initializeStorage();
58     }
59
60     public void setBlockUserDeletion(boolean blockUserDeletion) {
61         this.blockUserDeletion = blockUserDeletion;
62     }
63
64     @Override
65     public void createUser(String username, byte[] publicKey) {
66         logger.trace("create user {}", username);
67         final String userKey = createUserKey(username);
68         final String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey);
69         storage.put(userKey, encodedPublicKey);
70         logger.trace("stored user key {} with value {}", userKey, encodedPublicKey);
71     }
72
73     @Override
74     public String getMac() {
75         return mac;
76     }
77
78     public void setMac(String mac) {
79         this.mac = mac;
80     }
81
82     @Override
83     public String getPin() {
84         return pin;
85     }
86
87     public void setPin(String pin) {
88         this.pin = pin;
89     }
90
91     @Override
92     public String getSetupId() {
93         return setupId;
94     }
95
96     public void setSetupId(String setupId) {
97         this.setupId = setupId;
98     }
99
100     @Override
101     public byte[] getPrivateKey() {
102         return privateKey;
103     }
104
105     @Override
106     public BigInteger getSalt() {
107         return salt;
108     }
109
110     @Override
111     public byte[] getUserPublicKey(String username) {
112         final String encodedKey = (String) storage.get(createUserKey(username));
113         if (encodedKey != null) {
114             return Base64.getDecoder().decode(encodedKey);
115         } else {
116             return null;
117         }
118     }
119
120     @Override
121     public void removeUser(String username) {
122         logger.trace("remove user {}", username);
123         if (!this.blockUserDeletion) {
124             storage.remove(createUserKey(username));
125         } else {
126             logger.debug("deletion of the user was blocked by binding settings");
127         }
128     }
129
130     @Override
131     public boolean hasUser() {
132         Collection<String> keys = storage.getKeys();
133         return keys.stream().anyMatch(this::isUserKey);
134     }
135
136     public void clear() {
137         logger.trace("clear all users");
138         if (!this.blockUserDeletion) {
139             for (String key : new HashSet<>(storage.getKeys())) {
140                 if (isUserKey(key)) {
141                     storage.remove(key);
142                 }
143             }
144         } else {
145             logger.debug("deletion of users information was blocked by binding settings");
146         }
147     }
148
149     private String createUserKey(String username) {
150         return STORAGE_USER_PREFIX + username;
151     }
152
153     private boolean isUserKey(String key) {
154         return key.startsWith(STORAGE_USER_PREFIX);
155     }
156
157     private void initializeStorage() throws InvalidAlgorithmParameterException {
158         mac = (String) storage.get(STORAGE_MAC);
159         final @Nullable Object saltConfig = storage.get(STORAGE_SALT);
160         final @Nullable Object privateKeyConfig = storage.get(STORAGE_PRIVATE_KEY);
161         if (mac == null) {
162             logger.warn(
163                     "could not find existing MAC in {}. Generating new MAC. This will require re-pairing of iOS devices.",
164                     storage.getClass().getName());
165             mac = HomekitServer.generateMac();
166             storage.put(STORAGE_MAC, mac);
167         }
168         if (saltConfig == null) {
169             salt = HomekitServer.generateSalt();
170             storage.put(STORAGE_SALT, salt.toString());
171         } else {
172             salt = new BigInteger(saltConfig.toString());
173         }
174         if (privateKeyConfig == null) {
175             privateKey = HomekitServer.generateKey();
176             storage.put(STORAGE_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey));
177         } else {
178             privateKey = Base64.getDecoder().decode(privateKeyConfig.toString());
179         }
180     }
181 }