1//
2// TORAuthKey.m
3// Tor
4//
5// Created by Benjamin Erhart on 29.09.21.
6//
7
8#import "TORAuthKey.h"
9
10@implementation TORAuthKey
11
12- (instancetype)initFromUrl:(NSURL *)url
13{
14 if ((self = [super init]))
15 {
16 NSError *error;
17 NSString *raw = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
18
19 if (error)
20 {
21 NSLog(@"[%@] Error while setting key: %@", NSStringFromClass(self.class), error.localizedDescription);
22
23 return nil;
24 }
25
26 _file = url;
27
28 NSMutableArray *parts = [raw componentsSeparatedByString:@":"].mutableCopy;
29
30 NSString *piece = [TORAuthKey getNextPieceOf:parts];
31 if (!piece) return nil;
32
33 if (self.isPrivate) {
34 _onionAddress = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"http://%@.onion", piece]];
35
36 piece = [TORAuthKey getNextPieceOf:parts];
37 if (!piece) return nil;
38 }
39
40 _authType = piece;
41
42 piece = [TORAuthKey getNextPieceOf:parts];
43 if (!piece) return nil;
44 _keyType = piece;
45
46 piece = [TORAuthKey getNextPieceOf:parts];
47 if (!piece) return nil;
48 _key = piece;
49 }
50
51 return self;
52}
53
54- (instancetype)initFromFile:(NSString *)path
55{
56 return [self initFromUrl:[NSURL fileURLWithPath:path]];
57}
58
59- (instancetype)initPrivate:(NSString * _Nonnull)key forDomain:(NSURL * _Nonnull)url
60{
61 if ((self = [super init]))
62 {
63 NSString *name = url.host.stringByDeletingPathExtension;
64
65 if (!name || [name isEqualToString:@""]) name = [NSUUID UUID].UUIDString;
66
67 _file = [[NSURL alloc] initFileURLWithPath:[NSString stringWithFormat:@"%@.auth_private", name]];
68 _onionAddress = url;
69 _authType = @"descriptor"; // Currently the only allowed value.
70 _keyType = @"x25519"; // Currently the only allowed value.
71 _key = key;
72 }
73
74 return self;
75}
76
77- (instancetype)initPublic:(NSString * _Nonnull)key withName: (NSString * _Nonnull)name
78{
79 if ((self = [super init]))
80 {
81 if (!name || [name isEqualToString:@""]) name = [NSUUID UUID].UUIDString;
82
83 _file = [[NSURL alloc] initFileURLWithPath:[NSString stringWithFormat:@"%@.auth", name]];
84 _onionAddress = nil;
85 _authType = @"descriptor"; // Currently the only allowed value.
86 _keyType = @"x25519"; // Currently the only allowed value.
87 _key = key;
88 }
89
90 return self;
91}
92
93
94// MARK: Public Methods
95
96- (BOOL)isPrivate
97{
98 return [_file.pathExtension isEqualToString:@"auth_private"];
99}
100
101- (void)setDirectory:(NSURL *)directory
102{
103 NSString *filename = _file.lastPathComponent;
104
105 if (filename)
106 {
107 NSURL *file = [directory URLByAppendingPathComponent:filename];
108
109 if (file) _file = file;
110 }
111}
112
113- (BOOL)persist
114{
115 NSError *error;
116 [self.description writeToURL:_file atomically:YES encoding:NSUTF8StringEncoding error:&error];
117
118 if (error)
119 {
120 NSLog(@"[%@] Error while persisting key: %@", NSStringFromClass(self.class), error.localizedDescription);
121 NSLog(@"%@", self.debugDescription);
122
123 return NO;
124 }
125
126 return YES;
127}
128
129
130- (NSString *)description
131{
132 if (self.isPrivate) {
133 // Spec says: "MUST NOT have the ".onion" suffix.
134
135 return [NSString stringWithFormat:@"%@:%@:%@:%@",
136 _onionAddress.host.stringByDeletingPathExtension,
137 _authType, _keyType, _key];
138 }
139
140 return [NSString stringWithFormat:@"%@:%@:%@", _authType, _keyType, _key];
141}
142
143- (NSString *)debugDescription
144{
145 return [NSString stringWithFormat:@"%@: file: %@, isPrivate: %d, onionAddress: %@, authType: %@, keyType: %@, key: %@",
146 NSStringFromClass(self.class), _file, self.isPrivate, _onionAddress, _authType, _keyType, _key];
147}
148
149
150// MARK: Equality
151
152- (BOOL)isEqualToAuthKey:(TORAuthKey *)authKey
153{
154 NSString *url2 = authKey.file.absoluteString;
155
156 if (!url2)
157 {
158 return NO;
159 }
160
161 return [_file.absoluteString isEqualToString:url2];
162}
163
164- (BOOL)isEqual:(id)object
165{
166 if (self == object)
167 {
168 return YES;
169 }
170
171 if (![object isKindOfClass:[TORAuthKey class]])
172 {
173 return NO;
174 }
175
176 return [self isEqualToAuthKey:object];
177}
178
179- (NSUInteger)hash
180{
181 return _file.hash;
182}
183
184
185// MARK: Class Methods
186
187+ (BOOL)isAuthFile:(NSURL *)url
188{
189 return [url.pathExtension isEqualToString:@"auth"] || [url.pathExtension isEqualToString:@"auth_private"];
190}
191
192
193// MARK: Private Methods
194
195+ (NSString *)getNextPieceOf:(NSMutableArray *)parts
196{
197 NSString *piece = parts.firstObject;
198
199 if (parts.count > 0) [parts removeObjectAtIndex: 0];
200
201 if (!piece) NSLog(@"Invalid format of auth key file!");
202
203 return piece;
204}
205
206
207@end