]> git.basschouten.com Git - openhab-addons.git/blob
ae164900b5b0364f36b8cd5222289d37cbb09272
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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<String> storage;
44     private String mac;
45     private BigInteger salt;
46     private byte[] privateKey;
47     private String pin;
48     private String setupId;
49
50     public HomekitAuthInfoImpl(Storage<String> storage, String pin, String setupId)
51             throws InvalidAlgorithmParameterException {
52         this.storage = storage;
53         this.pin = pin;
54         this.setupId = setupId;
55         initializeStorage();
56     }
57
58     @Override
59     public void createUser(String username, byte[] publicKey) {
60         logger.trace("Create user {}", username);
61         final String userKey = createUserKey(username);
62         final String encodedPublicKey = Base64.getEncoder().encodeToString(publicKey);
63         storage.put(userKey, encodedPublicKey);
64         logger.trace("Stored user key {} with value {}", userKey, encodedPublicKey);
65     }
66
67     @Override
68     public String getMac() {
69         return mac;
70     }
71
72     public void setMac(String mac) {
73         this.mac = mac;
74     }
75
76     @Override
77     public String getPin() {
78         return pin;
79     }
80
81     public void setPin(String pin) {
82         this.pin = pin;
83     }
84
85     @Override
86     public String getSetupId() {
87         return setupId;
88     }
89
90     public void setSetupId(String setupId) {
91         this.setupId = setupId;
92     }
93
94     @Override
95     public byte[] getPrivateKey() {
96         return privateKey;
97     }
98
99     @Override
100     public BigInteger getSalt() {
101         return salt;
102     }
103
104     @Override
105     public byte[] getUserPublicKey(String username) {
106         final String encodedKey = storage.get(createUserKey(username));
107         if (encodedKey != null) {
108             return Base64.getDecoder().decode(encodedKey);
109         } else {
110             return null;
111         }
112     }
113
114     @Override
115     public void removeUser(String username) {
116         logger.trace("Remove user {}", username);
117         storage.remove(createUserKey(username));
118     }
119
120     @Override
121     public boolean hasUser() {
122         Collection<String> keys = storage.getKeys();
123         return keys.stream().anyMatch(this::isUserKey);
124     }
125
126     public void clear() {
127         logger.trace("Clear all users");
128         for (String key : new HashSet<>(storage.getKeys())) {
129             if (isUserKey(key)) {
130                 storage.remove(key);
131             }
132         }
133     }
134
135     private String createUserKey(String username) {
136         return STORAGE_USER_PREFIX + username;
137     }
138
139     private boolean isUserKey(String key) {
140         return key.startsWith(STORAGE_USER_PREFIX);
141     }
142
143     private void initializeStorage() throws InvalidAlgorithmParameterException {
144         mac = storage.get(STORAGE_MAC);
145         final @Nullable Object saltConfig = storage.get(STORAGE_SALT);
146         final @Nullable Object privateKeyConfig = storage.get(STORAGE_PRIVATE_KEY);
147         if (mac == null) {
148             logger.warn(
149                     "Could not find existing MAC in {}. Generating new MAC. This will require re-pairing of iOS devices.",
150                     storage.getClass().getName());
151             mac = HomekitServer.generateMac();
152             storage.put(STORAGE_MAC, mac);
153         }
154         if (saltConfig == null) {
155             salt = HomekitServer.generateSalt();
156             storage.put(STORAGE_SALT, salt.toString());
157         } else {
158             salt = new BigInteger(saltConfig.toString());
159         }
160         if (privateKeyConfig == null) {
161             privateKey = HomekitServer.generateKey();
162             storage.put(STORAGE_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey));
163         } else {
164             privateKey = Base64.getDecoder().decode(privateKeyConfig.toString());
165         }
166     }
167 }