├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── .travis.yml ├── AUTHORS ├── LICENSE ├── docs ├── architecture │ ├── class_overview.png │ ├── components.md │ ├── components_rough.png │ └── control_flow.md ├── bft │ └── overview.md ├── examples │ ├── hosts │ └── s3cfg └── overview.md ├── pom.xml ├── readme.md └── src ├── main └── java │ └── at │ └── ac │ └── ait │ └── archistar │ ├── backendserver │ ├── ExecutionHandler.java │ ├── OzymandiasCommandHandler.java │ ├── OzymandiasServer.java │ ├── SecurityMonitor.java │ ├── ServerServerCommunication.java │ ├── fragments │ │ ├── Fragment.java │ │ └── RemoteFragment.java │ └── storageinterface │ │ ├── DisconnectedException.java │ │ ├── FilesystemStorage.java │ │ ├── InvalidFragmentNameException.java │ │ ├── JetS3tStorage.java │ │ ├── MemoryStorage.java │ │ └── StorageServer.java │ ├── engine │ ├── Engine.java │ ├── TestEngine.java │ ├── crypto │ │ ├── ArchistarSMCIntegrator.java │ │ └── PseudoMirrorCryptoEngine.java │ ├── dataobjects │ │ ├── CustomSerializer.java │ │ ├── FSObject.java │ │ ├── Serializer.java │ │ ├── SimpleFile.java │ │ └── SimpleFileInterface.java │ ├── distributor │ │ ├── BFTDistributor.java │ │ ├── Distributor.java │ │ ├── ServerConfiguration.java │ │ └── TestServerConfiguration.java │ ├── messages │ │ ├── ReadCommand.java │ │ └── WriteCommand.java │ ├── metadata │ │ ├── MetadataService.java │ │ └── SimpleMetadataService.java │ ├── serverinterface │ │ ├── OzymandiasClient.java │ │ └── OzymandiasClientHandler.java │ └── userinterface │ │ ├── ArchistarS3.java │ │ ├── FakeBucket.java │ │ ├── FakeRoot.java │ │ ├── RedirectorFilter.java │ │ └── XmlDocumentBuilder.java │ └── trustmanager │ ├── SSLContextFactory.java │ ├── SecureKeyStore.java │ └── TrustManagerFactory.java └── test └── java └── at └── ac └── ait └── archistar ├── cryptoengine ├── MirrorTest.java └── TestSecretSharingCryptoEngine.java ├── data └── CustomSerializerTest.java ├── integration ├── AbstractIntegrationTest.java ├── BftTest.java ├── EncryptedFileSystemTest.java ├── FileSystemTest.java └── MemoryOnlyTest.java ├── metadata └── SimpleDirectoryServiceTest.java └── storage ├── AbstractStorageTest.java ├── FileSystemStorageTest.java ├── JetS3tStorageTest.java └── MemoryStoreTest.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | /target 3 | config/currentView 4 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | archistar 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 3 | org.eclipse.jdt.core.compiler.compliance=1.7 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.7 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk7 4 | - openjdk7 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Archistar was originally created in 2013 at the Austrian 2 | Insititute of Technology (http://www.ait.ac.at), Vienna, 3 | Austria. 4 | 5 | The PRIMARY Authors (so far) are: 6 | 7 | * Andreas Happe (andreashappe@snikt.net) 8 | * Thomas Loruenser (thomas.loruenser@ait.ac.at) 9 | 10 | And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- 11 | people who have submitted patches, reported bugs, added translations, helped 12 | answer newbie questions, and generally made Archistar that much better: 13 | 14 | * Christian Hanser 15 | * Daniel Slamanig 16 | * Konrad Lanz 17 | * Peter Lipp 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /docs/architecture/class_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archistar/archistar-core/feb4b516cb8f262e23540fc869291cd1a8ff4b2b/docs/architecture/class_overview.png -------------------------------------------------------------------------------- /docs/architecture/components.md: -------------------------------------------------------------------------------- 1 | ![High-Level Component Diagram](https://raw.github.com/archistar/archistar-core/master/docs/architecture/components_rough.png "High-Level Component Diagram") 2 | 3 | The components can be structured in three big parts: frontend, middleware and backend. 4 | 5 | # Frontend 6 | 7 | This package provides a user-interface to client programs. It utilizes a (middleware) Engine instance for communicating with the network. Current client frontends include a FTP server and a disk-based sync utility. 8 | 9 | # Middleware 10 | 11 | The middleware is responsible for receiving messages from the frondend (via the SimpleUserInterface), splits and encrypts those messages and forwards them to the different backend servers. 12 | 13 | ## SimpleUserInterface 14 | 15 | This is the application interface to the Archistar system. It should provide an easy interface for application developers, thus it provides rather standard get/put/delete/list methods. 16 | 17 | 18 | ## MetadataService 19 | 20 | The metadata service is reponsible for mapping the end-user (unencrypted) filenames to (encrypted) blob ids on the the different storage servers. Through this it also defines which servers will be responsible for storing data fragments (== encrypted user data). 21 | 22 | In addition the MetadataService is responsible for providing metadata for files as well as handle delete operations (as the storage server themselves do not delete data for now) by querying or modifying their data index. 23 | 24 | To achieve it's storage operations the MetadataService is utilizing the Distributor component. 25 | 26 | Versioning is another feature which would be implemented by this component. 27 | 28 | ## CryptoEngine 29 | 30 | The CryptoEngine is responsible for en/decryption. In the encryption case it is provided the original data as well as placeholders for the resulting encrypted data (otherwise it would not know how many fragments to create). The decryption operation works in the opposite direction: it is provided multiple data fragments and should reconstruct the original data. 31 | 32 | ## Distributor 33 | 34 | The distributor is responsible for distributing the encrypted data fragments to the differnet server (in the write-case) as well as responsible for gathering the different encrypted fragments in the read-case. To achieve error resilence the BFTDistributor utilizes a BFT algorithm. 35 | 36 | ## ExecutionHandler 37 | 38 | Distributor uses a BFT network for sending operations to servers. The servers in turn contain a storage backend (StorageServer) which will be used to persist and query data. The ExecutionHandler interface is an callback interface which forwards requests from the BFT server to the storage system. To minimize requirements on the storage system currenlty only read and write operations are performed. The API was inspired by Key-Value-Stores. 39 | 40 | ## Serializer 41 | 42 | The Serializer converts a end-user FileObject (as provided by the SimpleFile interface) and converts it into a byte array. The deserialize routine performs this vice-versa. 43 | 44 | ## ServerConfiguration 45 | 46 | The ServerConfiguration interface is used to store information about all connected servers (and currently about the used StorageServer backend per BFT server). It is used by the MetadataService as well as by the Distributor to gain information about the current system configuration. 47 | 48 | If later versions support dynamic server configurations most of its imlpementation will be at the ServerConfiguration level. 49 | 50 | ## SecurityMonitor 51 | 52 | This is a centralized singleton component where all (assumed) security breaches are reported. It in turn is reponsible for policing and (potentially) escalating a security notification into a system shutdown. 53 | 54 | # Backend 55 | 56 | The backend package ipmlements active backend servers. They accept commands from the middleware (via the bft network) and forward the ordered requests to the included StorageServer. 57 | 58 | ## StorageServer 59 | 60 | The StorageServer interface represents a storage system where encrypted fragments (BLOBs acutally) will be stored. Currently implementations include Memory, Filesystem or S3-based storage engines. 61 | 62 | # Bft 63 | 64 | This includes the abstract bft state machine which will be executed within each backend server. 65 | 66 | Future releases might move the bft package either into the backend package or into a library of it's own. 67 | 68 | # TrustManager 69 | 70 | Global trust manager that manages the certificates needed for establishing secure connections between replicas. Future releases might merge the ServerConfiguration object and the TrustManager. 71 | -------------------------------------------------------------------------------- /docs/architecture/components_rough.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Archistar/archistar-core/feb4b516cb8f262e23540fc869291cd1a8ff4b2b/docs/architecture/components_rough.png -------------------------------------------------------------------------------- /docs/architecture/control_flow.md: -------------------------------------------------------------------------------- 1 | # Control/Data Flow 2 | 3 | 4 | ## Write Operation 5 | 6 | The data flow in the write-case is: 7 | 8 | 1. User application uses SimpleFileInterface to communicate with Engine 9 | 2. Engine uses MetadataService to gain information on which server under which filenames the encrypted fragments should be stored 10 | 3. CryptoEngine gets the original data, performs encryption and puts encrypted data into the placeholders (provided by MetadataService) 11 | 4. Distributor gets the encrypted data and forwards the request to the different backend servers 12 | 5. Within the backend server the ExecutionHandler is called which forwards the storage operation to StorageServer 13 | 6. StorageServer persists the data 14 | 15 | ## Read Operation 16 | 17 | The data flow for the read-case is similar: 18 | 19 | 1. User application uses SimpleFileInterface to communicate with Engine 20 | 2. Engine uses MetadataService to gain informaiton on which server under which filenames the encrypted fragments should be stored 21 | 3. Distributor gets the read request and forwards the information through the BFT network to the backend servers 22 | 4. Within the backend server the ExecutionHandler is called to perform the storage operation through StorageServer 23 | 5. StorageServer retrieves the data (might be a network operation in case of S3) and returns it to the Distributor 24 | 6. After the Distributor has collected 2f+1 results the result is forwarded to the CryptoEngine 25 | 7. CryptoEngine reconstructs the original data 26 | 8. Engine returns the original (unencrypted) data to the caller 27 | -------------------------------------------------------------------------------- /docs/bft/overview.md: -------------------------------------------------------------------------------- 1 | # BFT / Distributor / Ozzymandias 2 | 3 | ## Overview 4 | 5 | - we're using BFT as message transport layer 6 | - BFT provides total ordered multicast and through that consensus between replicas (servers) 7 | - based upon [PBFT](http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&ved=0CDoQFjAB&url=http%3A%2F%2Fpmg.csail.mit.edu%2Fpapers%2Fosdi99.pdf&ei=o55iUqGuFYrdtAbdwIHYAQ&usg=AFQjCNFd4C-1HEEfle_N2jBAoK74B9A1gg&bvm=bv.54934254,d.Yms). If you have only time to read one paper, please read [Aardvark](http://www.cs.utexas.edu/~aclement/aardvark-tr.pdf) 8 | - not optimized for performance (yet) 9 | - message-based, uses [netty.io](http://netty.io) for message transport 10 | - *Distributor* is the Archistar server-side component (which in turn is a client within the BFT network). 11 | - *Ozzymandias* ([*"My name is Ozymandias, king of kings: Look on my works, ye Mighty, and despair!"*](http://en.wikipedia.org/wiki/Ozymandias)) is the internal name for the BFT-Network. Thus OzzymandiasServer and OzzymandiasClient. 12 | - An operation-in-progress is called *Transaction* 13 | - The *ServerServerCommunication* class is a 'virtual' client which is instantiated at each server. The servers use this to send/broadcast commands to all replicas. 14 | 15 | ## Assumptions / Changes vs. PBFT 16 | 17 | - we use TLS for server authentication as well as for verifying message authenticity -- BUT we're currently bypassing identity verification (look into TrustManager and SecureKeyStore if you want to improve this situation). So we're suffering the performance hit (which makes our prototype's performance comparable to full-blown solutions) but are not reaping MAC benefits 18 | - initial data transfer from the client to the replicas differs from BFT: as we are planing of implementing a secret-sharing network passing all data to one replica doesn't make too much sense security-wise. This also means that currenly byzantine clients can deliver garbage to the replicas: future versions will employ verifiy-able secret sharing to solve this drawback. 19 | - view-change protocol is minimal and untested for now 20 | - no catch-up messages (which would improve performance in case of errors) for now 21 | - no time-bounded state transfer. In case of error we notify the SecurityManager component. Which in turn just shuts down the network 22 | 23 | ## Implementation notes: Uglyness ahead 24 | 25 | - An instance of the *Transaction* class includes all state information for one Operation. Alas this makes this class rather large. 26 | - Transactions are stored in two collections: one TreeMap which orders Transactions by client-id, one TreeMap which uses the BFT-network-internal Id. If we wouldn't care about performance we would just use a in-memory SQL-database to make the queries easier to understand. 27 | - We need those two Collections as the initial communication between client and a replica does not contain the internal transaction-id. Both (transaction-id and client-id) are only known for sure after retrieving a PREPARE command. 28 | - Transaction assembly is somehow split between *OzzymandiasServer*, *OzymandiasCommandHandler* and *Transaction*. This is ugly. 29 | - asynchronous vs synchronous communication patterns: the server are using asynchronous message-passing (good), the clients expect typical UNIX synchronous message patterns (meh.). This leads to clients using a WaitCondition for each sent command/request 30 | 31 | -------------------------------------------------------------------------------- /docs/examples/hosts: -------------------------------------------------------------------------------- 1 | 127.0.0.1 localhost 2 | 127.0.1.1 MobileBastard 3 | 127.0.0.1 s3.amazonaws.com 4 | 127.0.0.1 s3.localhost 5 | 6 | # The following lines are desirable for IPv6 capable hosts 7 | ::1 ip6-localhost ip6-loopback 8 | fe00::0 ip6-localnet 9 | ff00::0 ip6-mcastprefix 10 | ff02::1 ip6-allnodes 11 | ff02::2 ip6-allrouters 12 | -------------------------------------------------------------------------------- /docs/examples/s3cfg: -------------------------------------------------------------------------------- 1 | [default] 2 | access_key = 3 | bucket_location = US 4 | cloudfront_host = cloudfront.amazonaws.com 5 | default_mime_type = binary/octet-stream 6 | delete_removed = False 7 | dry_run = False 8 | enable_multipart = True 9 | encoding = UTF-8 10 | encrypt = False 11 | follow_symlinks = False 12 | force = False 13 | get_continue = False 14 | gpg_command = /usr/bin/gpg 15 | gpg_decrypt = %(gpg_command)s -d --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s 16 | gpg_encrypt = %(gpg_command)s -c --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s 17 | gpg_passphrase = 18 | guess_mime_type = True 19 | host_base = localhost:8080 20 | host_bucket = %(bucket)s.localhost:8080 21 | human_readable_sizes = False 22 | invalidate_on_cf = False 23 | list_md5 = False 24 | log_target_prefix = 25 | mime_type = 26 | multipart_chunk_size_mb = 15 27 | preserve_attrs = True 28 | progress_meter = True 29 | proxy_host = 30 | proxy_port = 0 31 | recursive = False 32 | recv_chunk = 4096 33 | reduced_redundancy = False 34 | secret_key = 35 | send_chunk = 4096 36 | simpledb_host = sdb.amazonaws.com 37 | skip_existing = False 38 | socket_timeout = 300 39 | urlencoding_mode = normal 40 | use_https = False 41 | verbosity = WARNING 42 | website_endpoint = http://%(bucket)s.localhost:8080/ 43 | website_error = 44 | website_index = index.html 45 | -------------------------------------------------------------------------------- /docs/overview.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | * [What are the high-level components](architecture/components.md) 4 | * [How is data routed through Archistar](architecture/control_flow.md) 5 | 6 | # Distributor / Ozzymandias / BFT 7 | 8 | * [Development notes](bft/overview.md) 9 | 10 | # Testing 11 | 12 | * [How to setup local S3 server](http://www.snikt.net/blog/2013/10/20/fakes3/) (external post, written by Andreas Happe) 13 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | at.ac.ait.archistar 5 | archistar 6 | jar 7 | 0.0.2-wip 8 | archistar 9 | http://maven.apache.org 10 | 11 | 12 | internal 13 | http://sqt.ait.ac.at/archistar/repository 14 | 15 | 16 | jboss 17 | http://repository.jboss.org/nexus/content/groups/public/ 18 | 19 | 20 | 21 | UTF-8 22 | 23 | 24 | 25 | 26 | junit 27 | junit 28 | 4.11 29 | test 30 | 31 | 32 | 33 | commons-io 34 | commons-io 35 | 1.3.2 36 | 37 | 38 | 39 | org.mockito 40 | mockito-all 41 | 1.8.4 42 | 43 | 44 | 45 | com.google.code.findbugs 46 | annotations 47 | 3.0.0 48 | 49 | 50 | 51 | org.easytesting 52 | fest-assert-core 53 | 2.0M10 54 | 55 | 56 | 57 | net.java.dev.jets3t 58 | jets3t 59 | 0.9.0 60 | 61 | 62 | 63 | org.slf4j 64 | slf4j-simple 65 | 1.7.5 66 | 67 | 68 | org.slf4j 69 | slf4j-api 70 | 1.7.5 71 | 72 | 73 | 74 | 75 | org.jboss.resteasy 76 | resteasy-jaxrs 77 | 3.0.6.Final 78 | 79 | 80 | org.jboss.resteasy 81 | resteasy-netty 82 | 3.0.6.Final 83 | 84 | 85 | 86 | 87 | io.netty 88 | netty-all 89 | 4.0.18.Final 90 | 91 | 92 | at.archistar 93 | archistar-bft 94 | 0.2-wip 95 | jar 96 | 97 | 98 | 99 | 100 | at.archistar 101 | archistar-smc 102 | 0.2-wip 103 | 104 | 105 | 106 | 107 | 108 | org.codehaus.mojo 109 | findbugs-maven-plugin 110 | 3.0.0 111 | 112 | Max 113 | Low 114 | true 115 | ${project.build.directory}/findbugs 116 | 117 | 118 | 119 | analyze-compile 120 | compile 121 | 122 | check 123 | 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-compiler-plugin 130 | 2.0.2 131 | 132 | 1.7 133 | 1.7 134 | 135 | 136 | 137 | org.apache.maven.plugins 138 | maven-surefire-plugin 139 | 2.16 140 | 141 | 142 | at/ac/ait/archistar/storage/JetS3tStorageTest.java 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | org.apache.maven.plugins 152 | maven-javadoc-plugin 153 | 2.9 154 | 155 | 156 | org.apache.maven.plugins 157 | maven-dependency-plugin 158 | 2.8 159 | 160 | 161 | 162 | 163 | Austrian Institute of Technology 164 | http://www.ait.ac.at 165 | 166 | bla bla bla 167 | 168 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Note: we're currently focusing our development effort on a new generation of the software which can also be [found on gitlab](http://github.com/Archistar/archistar-akka) 2 | 3 | The new version is currently a WIP, we do have an updated secret-sharing library as well as a working prototype of the bft-and-secret-sharing distribution component. Missing is an "usable" end-user interface as well as actually storing data. 4 | 5 | # Archistar [![Build Status](https://travis-ci.org/Archistar/archistar-core.png?branch=master)](https://travis-ci.org/Archistar/archistar-core) 6 | 7 | _"hackable secure multi-cloud solution (or at least a prototype)"_ 8 | 9 | Archistar is a multi-cloud prototype written during the *tada* Archistar project. The goal is to distribute user data to different (private) clouds in a way that allows no single cloud provider to read or alter an user's data. 10 | 11 | ## Some background 12 | 13 | - secure cloud-based storage 14 | - uses [archistar-bft](https://github.com/archistar/archistar-bft) BFT library for achieving total ordering 15 | - uses [netty.io](http://netty.io) as network transport 16 | - uses [archistar-smc](https://github.com/archistar/archistar-smc) for secret sharing, currently test-cases utilize Sharmir's Secret Sharing 17 | - Multiple backend storage options (memory-only, file-backed, S3-backed) 18 | - TLS authentication and encryption between communication partners -- key validation is not implemented yet 19 | - client-side S3 interface 20 | 21 | ## Design Goals 22 | 23 | - well-written code 24 | - hackability: other projects should be able to use archistar as starting point 25 | - k.i.s.s. 26 | 27 | ## Running archistar 28 | 29 | This example uses the ftp demostrator which consists of four locally started storage servers. Each of the storage servers uses a local filesystem backend for data storage (which can be found under `/var/spool/archistar/test-s3/1..4`). 30 | 31 | First start the fake-S3 server frontend: 32 | 33 | ```bash 34 | $ mvn exec:java -Dexec.mainClass="at.ac.ait.archistar.frontend.ArchistarS3" 35 | [INFO] Scanning for projects... 36 | [INFO] Searching repository for plugin with prefix: 'exec'. 37 | [INFO] ------------------------------------------------------------------------ 38 | [INFO] Building archistar 39 | [INFO] task-segment: [exec:java] 40 | [INFO] ------------------------------------------------------------------------ 41 | [INFO] Preparing exec:java 42 | [INFO] No goals needed for project - skipping 43 | [INFO] [exec:java {execution: default-cli}] 44 | [at.ac.ait.archistar.frontend.ArchistarS3.main()] INFO at.ac.ait.archistar.frontend.ArchistarS3 - Starting archistar storage engine 45 | [Thread-7] INFO at.ac.ait.archistar.bft.BftEngine - successful transactions: 0.0 46 | [Thread-7] INFO at.ac.ait.archistar.bft.BftEngine - server: 0 transaction length: 0ms 47 | ``` 48 | 49 | To use the s3cmd program to access the server you'll need to adopt your /etc/hosts configuration file. The s3cmd uses hard-coded amazon server urls so we redirect these to our local server by adding the following lines to `/etc/hosts` (an example configuration file can be found at docs/examples/hosts): 50 | 51 | ``` 52 | 127.0.0.1 s3.amazonaws.com 53 | 127.0.0.1 s3.localhost 54 | ``` 55 | 56 | s3cmd needs a configuration file, an example file can be found at docs/examples/s3cfg. 57 | 58 | Now you can use s3cmd to access the server (note that currently fake_bucket is hard coded, also I already had two files in my bucket): 59 | 60 | ``` bash 61 | $ s3cmd -c docs/examples/s3cfg ls s3://fake_bucket 62 | 2006-02-03 16:41 140 s3://fake_bucket/iptables-rule.txt 63 | 2006-02-03 16:41 1151 s3://fake_bucket/link-list.txt 64 | 65 | $ echo "testdata" > testfile 66 | $ md5sum testfile 67 | 73d643ec3f4beb9020eef0beed440ad0 testfile 68 | 69 | $ s3cmd -c docs/examples/s3cfg put testfile s3://fake_bucket/testfile 70 | testfile -> s3://fake_bucket/testfile [1 of 1] 71 | 9 of 9 100% in 0s 48.76 B/s done 72 | 73 | $ s3cmd -c docs/examples/s3cfg ls s3://fake_bucket 74 | 2006-02-03 16:41 140 s3://fake_bucket/iptables-rule.txt 75 | 2006-02-03 16:41 1151 s3://fake_bucket/link-list.txt 76 | 2006-02-03 16:41 9 s3://fake_bucket/testfile 77 | 78 | $ s3cmd -c docs/examples/s3cfg get s3://fake_bucket/testfile testfile.2 79 | s3://fake_bucket/testfile -> testfile.2 [1 of 1] 80 | 9 of 9 100% in 0s 416.49 B/s done 81 | 82 | $ md5sum testfile.2 83 | 73d643ec3f4beb9020eef0beed440ad0 testfile.2 84 | ``` 85 | 86 | ## Developing archistar 87 | 88 | 0. [read the documentation](docs/overview.md) 89 | 1. fork it 90 | 2. work on your new feature 91 | 3. run the testcases with `mvn test` 92 | 4. send me a pull request 93 | 94 | ## Contributors 95 | 96 | - Andreas Happe, AIT 97 | - Christian Hanser, TU Graz 98 | - Daniel Slamanig, TU Graz 99 | - Konrad Lanz, TU Graz 100 | - Peter Lipp, TU Graz 101 | - Thomas Loruenser, AIT 102 | 103 | ## License 104 | 105 | This project is licensed under the GPLv2. If you want to use Archistar under a different license please contact Thomas Loruenser . 106 | 107 | ## Citing Archistar 108 | 109 | If you find Archistar useful for your work or if you use Archistar in a project, paper, website, etc., 110 | please cite the software as 111 | 112 | T. Lorünser, A. Happe, D. Slamanig (2014). “ARCHISTAR – A framework for secure distributed storage”. GNU General Public License. http://ARCHISTAR.at 113 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/ExecutionHandler.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver; 2 | 3 | import at.ac.ait.archistar.backendserver.storageinterface.DisconnectedException; 4 | 5 | /** 6 | * These are the operations that BFT servers (replicas) will call to perform I/O 7 | * storage operations 8 | * 9 | * @author andy 10 | */ 11 | public interface ExecutionHandler { 12 | 13 | /** 14 | * write/persist data on the replica (note: this could also mean that the 15 | * replica uses some external storage as S3 to persist the data) 16 | * 17 | * @param fragmentid the unique id under which data should be stored 18 | * @param data the to be stored data 19 | * @return the stored data 20 | * @throws DisconnectedException 21 | */ 22 | public byte[] putBlob(String fragmentid, byte[] data) throws DisconnectedException; 23 | 24 | /** 25 | * retrieve persisted data given a storage id (fragment id) 26 | * 27 | * NOTE: i need to rewrite this to make error cases clearer (how to signal 28 | * if there was no stored data given a fragment id, etc) 29 | * 30 | * @param fragmentid which data should be retrieved 31 | * @return the retrieved data 32 | * @throws DisconnectedException 33 | */ 34 | public byte[] getBlob(String fragmentid) throws DisconnectedException; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/OzymandiasCommandHandler.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.SimpleChannelInboundHandler; 8 | import io.netty.channel.ChannelHandler.Sharable; 9 | import at.archistar.bft.messages.AbstractCommand; 10 | import at.archistar.bft.messages.ClientCommand; 11 | import at.archistar.bft.messages.IntraReplicaCommand; 12 | 13 | /** 14 | * This is a netty.io message handler; it will be called for each retrieved 15 | * exception or message (on the bft server). 16 | * 17 | * note: I wanted to integrate this into OzymandiasServer but the attempt failed 18 | * due to some threading errors 19 | * 20 | * @author andy 21 | */ 22 | @Sharable 23 | public class OzymandiasCommandHandler extends SimpleChannelInboundHandler { 24 | 25 | private final Logger logger = LoggerFactory.getLogger(OzymandiasCommandHandler.class); 26 | 27 | /** 28 | * the replica this instance handles messages for 29 | */ 30 | private final OzymandiasServer parentSystem; 31 | 32 | public OzymandiasCommandHandler(OzymandiasServer ozzy) { 33 | super(); 34 | this.parentSystem = ozzy; 35 | } 36 | 37 | /** 38 | * retrieve one message and forward it to the corresponding server method 39 | * 40 | * @param ctx the context is used for establishing a return-channel for 41 | * signaling a client's operation result 42 | * @param msg the to be handled message 43 | */ 44 | @Override 45 | public void channelRead0(ChannelHandlerContext ctx, AbstractCommand msg) { 46 | 47 | logger.debug("server {} received {}", parentSystem.getReplicaId(), msg); 48 | 49 | if (msg instanceof IntraReplicaCommand) { 50 | this.parentSystem.processIntraReplicaCommand((IntraReplicaCommand) msg); 51 | } else if (msg instanceof ClientCommand) { 52 | this.parentSystem.setClientSession(((ClientCommand) msg).getClientId(), ctx); 53 | this.parentSystem.processClientCommand((ClientCommand) msg); 54 | } else { 55 | assert (false); 56 | } 57 | } 58 | 59 | /** 60 | * handle thrown exceptions 61 | * 62 | * TODO: actually handle them 63 | */ 64 | @Override 65 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 66 | logger.error("Unexpected exception from downstream.", cause); 67 | ctx.close(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/OzymandiasServer.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javax.net.ssl.SSLEngine; 7 | 8 | import at.ac.ait.archistar.backendserver.storageinterface.DisconnectedException; 9 | import at.ac.ait.archistar.engine.messages.ReadCommand; 10 | import at.ac.ait.archistar.engine.messages.WriteCommand; 11 | import at.ac.ait.archistar.trustmanager.SSLContextFactory; 12 | import at.archistar.bft.messages.AbstractCommand; 13 | import at.archistar.bft.messages.CheckpointMessage; 14 | import at.archistar.bft.messages.ClientCommand; 15 | import at.archistar.bft.messages.IntraReplicaCommand; 16 | import at.archistar.bft.messages.TransactionResult; 17 | import at.archistar.bft.server.BftEngine; 18 | import at.archistar.bft.server.BftEngineCallbacks; 19 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 20 | import io.netty.bootstrap.ServerBootstrap; 21 | import io.netty.channel.ChannelFuture; 22 | import io.netty.channel.ChannelHandlerContext; 23 | import io.netty.channel.ChannelInitializer; 24 | import io.netty.channel.ChannelOption; 25 | import io.netty.channel.EventLoopGroup; 26 | import io.netty.channel.nio.NioEventLoopGroup; 27 | import io.netty.channel.socket.SocketChannel; 28 | import io.netty.channel.socket.nio.NioServerSocketChannel; 29 | import io.netty.handler.codec.serialization.ClassResolvers; 30 | import io.netty.handler.codec.serialization.ObjectDecoder; 31 | import io.netty.handler.codec.serialization.ObjectEncoder; 32 | import io.netty.handler.ssl.SslHandler; 33 | 34 | /** 35 | * This is the main archistar/bft server application logic 36 | * 37 | * @author andy 38 | */ 39 | public class OzymandiasServer implements Runnable, BftEngineCallbacks { 40 | 41 | /** 42 | * my internal server id 43 | */ 44 | private final int serverId; 45 | 46 | /** 47 | * a serverid -> port mapping for our servers 48 | */ 49 | private final Map serverList; 50 | 51 | /** 52 | * upon which port is the server listening 53 | */ 54 | private final int port; 55 | 56 | private final Map clientMap = new HashMap<>(); 57 | 58 | /** 59 | * used for server-to-server communication 60 | */ 61 | private final ServerServerCommunication servers; 62 | 63 | /** 64 | * the executor is responsible for actually performing data I/O operations 65 | * on the server 66 | */ 67 | private final ExecutionHandler executor; 68 | 69 | private final EventLoopGroup bossGroup; 70 | 71 | private final EventLoopGroup workerGroup; 72 | 73 | /** 74 | * this is the listening server channel (will be configured to use the 75 | * configured server port) 76 | */ 77 | private ChannelFuture serverChannel; 78 | 79 | /** 80 | * this encapsulates the server's BFT state. It is responsible for creating 81 | * a distributed "shared" state between all replicas/servers 82 | */ 83 | private final BftEngine bftEngine; 84 | 85 | private final SecurityMonitor secMonitor; 86 | 87 | /** 88 | * max message size in bytes 89 | */ 90 | public final static int maxObjectSize = 10 * 1024 * 1024; 91 | 92 | public OzymandiasServer(int myServerId, Map serverList, int f, ExecutionHandler executor, NioEventLoopGroup bossGroup, NioEventLoopGroup workerGroup) { 93 | this.bossGroup = bossGroup; 94 | this.workerGroup = workerGroup; 95 | 96 | this.serverList = serverList; 97 | this.serverId = myServerId; 98 | this.port = this.serverList.get(serverId); 99 | this.servers = new ServerServerCommunication(serverId, this.serverList, this.workerGroup); 100 | this.executor = executor; 101 | this.secMonitor = new SecurityMonitor(this); 102 | this.bftEngine = new BftEngine(serverId, f, this); 103 | } 104 | 105 | /** 106 | * setup the server listening ports and handler routines 107 | */ 108 | @Override 109 | @SuppressFBWarnings("SIC_INNER_SHOULD_BE_STATIC_ANON") 110 | public void run() { 111 | /* start up netty listener */ 112 | final OzymandiasCommandHandler handler = new OzymandiasCommandHandler(this); 113 | 114 | try { 115 | ServerBootstrap b = new ServerBootstrap(); 116 | 117 | b.group(bossGroup, workerGroup) 118 | .channel(NioServerSocketChannel.class) 119 | .childHandler(new ChannelInitializer() { 120 | @Override 121 | public void initChannel(SocketChannel ch) throws Exception { 122 | SSLEngine engine = SSLContextFactory.getServerContext().createSSLEngine(); 123 | engine.setUseClientMode(false); 124 | 125 | ch.pipeline().addLast( 126 | new SslHandler(engine), 127 | new ObjectEncoder(), 128 | new ObjectDecoder(maxObjectSize, ClassResolvers.cacheDisabled(null)), 129 | handler); 130 | } 131 | }).option(ChannelOption.SO_BACKLOG, 128) 132 | .childOption(ChannelOption.SO_KEEPALIVE, true); 133 | 134 | // Bind and start to accept incoming connections. 135 | serverChannel = b.bind(port).sync(); 136 | 137 | // wait until the server socket is closed 138 | serverChannel.channel().closeFuture().sync(); 139 | } catch (InterruptedException e) { 140 | e.printStackTrace(); 141 | } finally { 142 | bossGroup.shutdownGracefully(); 143 | workerGroup.shutdownGracefully(); 144 | } 145 | } 146 | 147 | /** 148 | * connect to all configured BFT replicas 149 | * @throws java.lang.InterruptedException 150 | */ 151 | public void connectServers() throws InterruptedException { 152 | this.servers.connect(); 153 | } 154 | 155 | /** 156 | * shutdown server process (listeners and handlers 157 | */ 158 | public void shutdown() { 159 | ChannelFuture cf = serverChannel.channel().close(); 160 | cf.awaitUninterruptibly(); 161 | } 162 | 163 | /** 164 | * return the servers id 165 | * 166 | * @return server replica id 167 | */ 168 | public int getReplicaId() { 169 | return this.serverId; 170 | } 171 | 172 | /** 173 | * this handler is called by the BFT engine as soon as a command can be 174 | * executed upon a replica (as it was received on enough replicas in the 175 | * same order) 176 | * 177 | * @param cmd the to be executed command 178 | * @return the executed commands result 179 | */ 180 | @Override 181 | public byte[] executeClientCommand(ClientCommand cmd) { 182 | byte[] binResult = null; 183 | 184 | try { 185 | if (cmd instanceof WriteCommand) { 186 | byte[] data = ((WriteCommand) cmd).getData(); 187 | binResult = this.executor.putBlob(((WriteCommand) cmd).getFragmentId(), data); 188 | } else if (cmd instanceof ReadCommand) { 189 | binResult = this.executor.getBlob(((ReadCommand) cmd).getFragmentId()); 190 | } 191 | } catch (DisconnectedException ex) { 192 | assert (false); 193 | } 194 | return binResult; 195 | } 196 | 197 | @Override 198 | public void invalidMessageReceived(AbstractCommand msg) { 199 | this.secMonitor.invalidMessageReceived(msg); 200 | } 201 | 202 | @Override 203 | public void replicasMightBeMalicous() { 204 | this.secMonitor.replicasMightBeMalicous(); 205 | } 206 | 207 | @Override 208 | public void sendToReplicas(IntraReplicaCommand cmd) { 209 | this.servers.send(cmd); 210 | } 211 | 212 | @Override 213 | public void invalidCheckpointMessage(CheckpointMessage msg) { 214 | this.secMonitor.invalidCheckpointMessage(msg); 215 | } 216 | 217 | @Override 218 | public void answerClient(TransactionResult transactionResult) { 219 | ChannelHandlerContext ctx = this.clientMap.get(transactionResult.getClientId()); 220 | ctx.writeAndFlush(transactionResult); 221 | } 222 | 223 | public void setClientSession(int clientId, ChannelHandlerContext ctx) { 224 | if (!this.clientMap.containsKey(clientId)) { 225 | this.clientMap.put(clientId, ctx); 226 | } 227 | } 228 | 229 | void processIntraReplicaCommand(IntraReplicaCommand intraReplicaCommand) { 230 | this.bftEngine.processIntraReplicaCommand(intraReplicaCommand); 231 | } 232 | 233 | void processClientCommand(ClientCommand clientCommand) { 234 | this.bftEngine.processClientCommand(clientCommand); 235 | } 236 | 237 | void tryAdvanceEra() { 238 | this.bftEngine.tryAdvanceEra(); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/SecurityMonitor.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import at.archistar.bft.messages.AbstractCommand; 7 | import at.archistar.bft.messages.CheckpointMessage; 8 | 9 | /** 10 | * This class responds to the different error situations 11 | * 12 | * TODO: maybe use singleton pattern for this? 13 | * 14 | * @author andy 15 | */ 16 | public class SecurityMonitor { 17 | 18 | private final Logger logger = LoggerFactory.getLogger(SecurityMonitor.class); 19 | 20 | private final OzymandiasServer myself; 21 | 22 | public SecurityMonitor(OzymandiasServer myself) { 23 | this.myself = myself; 24 | } 25 | 26 | /** 27 | * TODO: this should be called if the sequence nrs start to differ 28 | * 29 | * what to do: executed operations should be the same at 2f+1 replicas. As 30 | * they are the same everywhere we should be able to perform the missing 31 | * operations and continue 32 | * 33 | * everything before "2f+1 prepared operations" should be able to be redone 34 | * by resetting the transactions/operations state to INCOMING and forcing 35 | * the new primary to resend the PREPREPARE message with n = 36 | * max(sequence)+1. This will lead to holes in our sequence numbers -- but 37 | * then, this should not be a problem. 38 | * 39 | * @return 40 | */ 41 | public boolean replicasMightBeMalicous() { 42 | logger.warn("primary might be malicous?"); 43 | 44 | myself.tryAdvanceEra(); 45 | assert (false); 46 | return true; 47 | } 48 | 49 | public void unreachableCodePath() { 50 | assert (false); 51 | } 52 | 53 | public void localMalicousErrorDetected() { 54 | assert (false); 55 | } 56 | 57 | /** 58 | * TODO: this should be called if my execution log seems to be running 59 | * behind the other replicas 60 | * 61 | * @return 62 | */ 63 | public void myselfNeedsReplay() { 64 | assert (false); 65 | } 66 | 67 | /** 68 | * a checkpoint message was not consistent with already received checkpoint 69 | * messages 70 | * 71 | * @param msg 72 | */ 73 | public void invalidCheckpointMessage(CheckpointMessage msg) { 74 | /* wrong message data type */ 75 | logger.error("invalid checkpoint message server " + myself.getReplicaId() + " message:" + msg); 76 | assert (false); 77 | } 78 | 79 | public void invalidMessageReceived(AbstractCommand msg) { 80 | logger.error("invalid message received on " + myself.getReplicaId() + " message:" + msg); 81 | assert (false); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/ServerServerCommunication.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.ChannelFuture; 6 | import io.netty.channel.ChannelInitializer; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.handler.codec.serialization.ClassResolvers; 10 | import io.netty.handler.codec.serialization.ObjectDecoder; 11 | import io.netty.handler.codec.serialization.ObjectEncoder; 12 | import io.netty.handler.ssl.SslHandler; 13 | import io.netty.channel.socket.nio.NioSocketChannel; 14 | 15 | import java.util.HashSet; 16 | import java.util.Map; 17 | import java.util.Map.Entry; 18 | import java.util.Set; 19 | 20 | import javax.net.ssl.SSLEngine; 21 | 22 | import at.ac.ait.archistar.trustmanager.SSLContextFactory; 23 | import at.archistar.bft.messages.AbstractCommand; 24 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 25 | 26 | /** 27 | * This class is responsible for sending messages to all replicas currently 28 | * within the replica system. 29 | * 30 | * @author andy 31 | */ 32 | public class ServerServerCommunication { 33 | 34 | /** 35 | * mapping from replica id -> port. Note: currently only connections to localhost are supported 36 | */ 37 | private final Map serverList; 38 | 39 | /** 40 | * opened channels 41 | */ 42 | private final Set channels; 43 | 44 | /** 45 | * my server/replica Id -- this is used so that we are not connecting to 46 | * ourself 47 | */ 48 | private int myServerId = -1; 49 | 50 | private EventLoopGroup loopGroup = null; 51 | 52 | public ServerServerCommunication(int myId, Map serverList, EventLoopGroup elg) { 53 | this.serverList = serverList; 54 | this.channels = new HashSet<>(); 55 | this.myServerId = myId; 56 | this.loopGroup = elg; 57 | } 58 | 59 | /** 60 | * connects to all replicas 61 | * 62 | * @throws InterruptedException 63 | */ 64 | @SuppressFBWarnings("SIC_INNER_SHOULD_BE_STATIC_ANON") 65 | public void connect() throws InterruptedException { 66 | for (Entry e : this.serverList.entrySet()) { 67 | int replicaId = e.getKey(); 68 | int replicaPort = e.getValue(); 69 | 70 | if (replicaId != myServerId) { 71 | Bootstrap b = new Bootstrap(); 72 | b.group(loopGroup) 73 | .channel(NioSocketChannel.class) 74 | .handler(new ChannelInitializer() { 75 | @Override 76 | public void initChannel(SocketChannel ch) throws Exception { 77 | // enable SSL/TLS support 78 | SSLEngine engine = SSLContextFactory.getClientContext().createSSLEngine(); 79 | engine.setUseClientMode(true); 80 | 81 | ch.pipeline().addLast( 82 | new SslHandler(engine), 83 | new ObjectEncoder(), 84 | new ObjectDecoder(OzymandiasServer.maxObjectSize, ClassResolvers.cacheDisabled(null))); 85 | } 86 | }); 87 | 88 | /* wait till server is connected */ 89 | ChannelFuture f = null; 90 | do { 91 | f = b.connect("127.0.0.1", replicaPort); 92 | f.await(); 93 | } while (!(f.isDone() && f.isSuccess())); 94 | 95 | this.channels.add(f.sync().channel()); 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * sends a message to all connected replicas 102 | * 103 | * @param cmd the to be sent message 104 | */ 105 | public void send(AbstractCommand cmd) { 106 | for (Channel channel : this.channels) { 107 | channel.writeAndFlush(cmd); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/fragments/Fragment.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.fragments; 2 | 3 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 4 | 5 | /** 6 | * This is a standard fragment, ie. one piece of data on one storage server. 7 | * 8 | * @author andy 9 | */ 10 | public interface Fragment { 11 | 12 | public enum EncryptionScheme { 13 | NONE, 14 | SHAMIR 15 | }; 16 | 17 | /** 18 | * @param data the data that should be stored within the fragment 19 | * @return the stored data 20 | */ 21 | byte[] setData(byte[] data); 22 | 23 | /** 24 | * @return the data that we believe to be on the storage server 25 | */ 26 | byte[] getData(); 27 | 28 | /** 29 | * @return is this fragment already stored/synced with the backend? storage 30 | * server 31 | */ 32 | boolean isSynchronized(); 33 | 34 | /** 35 | * @param value set the synchronized state (is the fragment in-sync with the 36 | * storage server side representation) to true 37 | * @return the new state 38 | */ 39 | boolean setSynchronized(boolean value); 40 | 41 | /** 42 | * @return fragments ID upon the server 43 | */ 44 | String getFragmentId(); 45 | 46 | /** 47 | * @return returns the used storage server 48 | */ 49 | StorageServer getStorageServer(); 50 | 51 | /** 52 | * sets the fragment id 53 | * 54 | * @param string the new fragment id 55 | * @return the set fragment id 56 | */ 57 | String setFragmentId(String string); 58 | 59 | /** 60 | * which encryption scheme was used for this fragment 61 | * @return the used encryption scheme 62 | */ 63 | EncryptionScheme getEncryptionScheme(); 64 | 65 | /** 66 | * sets the to-be-used encryption scheme 67 | * 68 | * @param scheme the scheme which will be used to encrypt the data 69 | */ 70 | void setEncryptionScheme(EncryptionScheme scheme); 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/fragments/RemoteFragment.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.fragments; 2 | 3 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 | 6 | /** 7 | * minimal implementation of the Fragment interface for use with the Archistar 8 | * prototype 9 | * 10 | * @author andy 11 | */ 12 | public class RemoteFragment implements Fragment { 13 | 14 | private String fragmentId; 15 | 16 | private byte[] data; 17 | 18 | private boolean syncedToServer; 19 | 20 | private StorageServer server; 21 | 22 | private EncryptionScheme scheme; 23 | 24 | public RemoteFragment(String fragmentId) { 25 | this.fragmentId = fragmentId; 26 | this.syncedToServer = false; 27 | this.scheme = EncryptionScheme.NONE; 28 | } 29 | 30 | public RemoteFragment(String fragmentId, StorageServer server) { 31 | this.fragmentId = fragmentId; 32 | this.server = server; 33 | this.syncedToServer = false; 34 | this.scheme = EncryptionScheme.NONE; 35 | } 36 | 37 | @Override 38 | @SuppressFBWarnings("EI_EXPOSE_REP") 39 | public byte[] setData(byte[] data) { 40 | this.data = data.clone(); 41 | this.syncedToServer = false; 42 | return this.data; 43 | } 44 | 45 | @Override 46 | @SuppressFBWarnings("EI_EXPOSE_REP") 47 | public byte[] getData() { 48 | return this.data; 49 | } 50 | 51 | @Override 52 | public boolean isSynchronized() { 53 | return this.syncedToServer; 54 | } 55 | 56 | @Override 57 | public String getFragmentId() { 58 | return fragmentId; 59 | } 60 | 61 | @Override 62 | public StorageServer getStorageServer() { 63 | return this.server; 64 | } 65 | 66 | @Override 67 | public boolean setSynchronized(boolean value) { 68 | return this.syncedToServer = value; 69 | } 70 | 71 | @Override 72 | public String setFragmentId(String fragmentId) { 73 | return this.fragmentId = fragmentId; 74 | } 75 | 76 | @Override 77 | public EncryptionScheme getEncryptionScheme() { 78 | return this.scheme; 79 | } 80 | 81 | @Override 82 | public void setEncryptionScheme(EncryptionScheme scheme) { 83 | this.scheme = scheme; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/storageinterface/DisconnectedException.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.storageinterface; 2 | 3 | /** 4 | * this exception should be used if a connection to a peer server was 5 | * interrupted (or could not be established). Remote partners can include 6 | * replicas as well as remote storage servers (i.e. S3) 7 | * 8 | * @author andy 9 | */ 10 | public class DisconnectedException extends Exception { 11 | 12 | private static final long serialVersionUID = 3950283191217740878L; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/storageinterface/FilesystemStorage.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.storageinterface; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import org.apache.commons.io.IOUtils; 12 | 13 | /** 14 | * provides a storage server through a newly created (empty) filesystem 15 | * directory. Each fragment will be written into a file. 16 | * 17 | * @author andy 18 | */ 19 | public class FilesystemStorage implements StorageServer { 20 | 21 | private boolean connected = false; 22 | 23 | private final int internalBFTId; 24 | 25 | private final File baseFp; 26 | 27 | public FilesystemStorage(int bftId, File baseDir) { 28 | this.baseFp = baseDir; 29 | this.internalBFTId = bftId; 30 | } 31 | 32 | /* memory storage does not need any connect operation */ 33 | @Override 34 | public int connect() { 35 | if (!baseFp.isDirectory() || !baseFp.canWrite()) { 36 | assert (false); 37 | return -1; 38 | } else { 39 | this.connected = true; 40 | return 0; 41 | } 42 | } 43 | 44 | @Override 45 | public int disconnect() { 46 | this.connected = false; 47 | return 0; 48 | } 49 | 50 | @Override 51 | public byte[] putBlob(String id, byte[] blob) throws DisconnectedException { 52 | 53 | validateOnline(); 54 | 55 | /* store data */ 56 | File new_file = new File(baseFp, id); 57 | try (FileOutputStream out = new FileOutputStream(new_file)) { 58 | out.write(blob); 59 | } catch (IOException e) { 60 | assert (false); 61 | } 62 | return blob; 63 | } 64 | 65 | private void validateOnline() throws DisconnectedException { 66 | if (!this.connected) { 67 | throw new DisconnectedException(); 68 | } 69 | } 70 | 71 | private boolean checkExistenceOfId(String id) { 72 | /* now do a fp.list().contains?(filename) */ 73 | String[] files = baseFp.list(); 74 | for (String f : files) { 75 | if (f.equalsIgnoreCase(id)) { 76 | return true; 77 | } 78 | } 79 | 80 | return false; 81 | } 82 | 83 | @Override 84 | public byte[] getBlob(String id) throws DisconnectedException { 85 | 86 | validateOnline(); 87 | 88 | if (!checkExistenceOfId(id)) { 89 | return new byte[0]; 90 | } 91 | 92 | /* receive data */ 93 | File new_file = new File(baseFp, id); 94 | assert (new_file.exists()); 95 | assert (new_file.canRead()); 96 | 97 | try (FileInputStream in = new FileInputStream(new_file)) { 98 | return IOUtils.toByteArray(in); 99 | } catch (IOException ex) { 100 | assert (false); 101 | } 102 | 103 | return new byte[0]; 104 | } 105 | 106 | @Override 107 | public boolean isConnected() { 108 | return this.connected; 109 | } 110 | 111 | @Override 112 | public int getFragmentCount() throws DisconnectedException { 113 | validateOnline(); 114 | return baseFp.list().length; 115 | } 116 | 117 | @Override 118 | public String getId() { 119 | return "storage:///" + this.baseFp.getAbsolutePath(); 120 | } 121 | 122 | @Override 123 | public int getBFTId() { 124 | return this.internalBFTId; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/storageinterface/InvalidFragmentNameException.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.storageinterface; 2 | 3 | /** 4 | * this exception is thrown if the replica does not recognize a fragment id 5 | * 6 | * @author andy 7 | */ 8 | public class InvalidFragmentNameException extends Exception { 9 | 10 | private static final long serialVersionUID = -2279567565333516592L; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/storageinterface/JetS3tStorage.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.storageinterface; 2 | 3 | import java.io.IOException; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | import org.apache.commons.io.IOUtils; 7 | import org.apache.http.client.CredentialsProvider; 8 | import org.jets3t.service.Jets3tProperties; 9 | import org.jets3t.service.S3Service; 10 | import org.jets3t.service.S3ServiceException; 11 | import org.jets3t.service.ServiceException; 12 | import org.jets3t.service.impl.rest.httpclient.RestS3Service; 13 | import org.jets3t.service.model.S3Bucket; 14 | import org.jets3t.service.model.S3Object; 15 | import org.jets3t.service.model.StorageObject; 16 | import org.jets3t.service.security.AWSCredentials; 17 | 18 | /** 19 | * provide a storage provider utilizing a remote S3 storage provider 20 | * 21 | * @author andy 22 | */ 23 | public class JetS3tStorage implements StorageServer { 24 | 25 | private final AWSCredentials awsCredentials; 26 | 27 | private S3Service s3service; 28 | 29 | private S3Bucket s3bucket; 30 | 31 | private final String bucketId; 32 | 33 | private final int internalBFTId; 34 | 35 | public JetS3tStorage(int bftId, String awsAccessKey, String awsSecretKey, String bucketId) { 36 | awsCredentials = new AWSCredentials(awsAccessKey, awsSecretKey); 37 | this.bucketId = bucketId; 38 | this.internalBFTId = bftId; 39 | this.s3bucket = null; 40 | } 41 | 42 | @Override 43 | public byte[] putBlob(String id, byte[] blob) throws DisconnectedException { 44 | 45 | if (s3service == null || s3bucket == null) { 46 | throw new DisconnectedException(); 47 | } 48 | 49 | try { 50 | StorageObject obj = new S3Object(id, blob); 51 | s3service.putObject(s3bucket.getName(), obj); 52 | return blob; 53 | } catch (IOException | NoSuchAlgorithmException | ServiceException e) { 54 | throw new DisconnectedException(); 55 | } 56 | } 57 | 58 | @SuppressWarnings("deprecation") 59 | @Override 60 | public byte[] getBlob(String id) throws DisconnectedException { 61 | 62 | try { 63 | if (!fragmentExists(id)) { 64 | return new byte[0]; 65 | } 66 | S3Object obj = s3service.getObject(s3bucket, id); 67 | return IOUtils.toByteArray(obj.getDataInputStream()); 68 | } catch (ServiceException | IOException e) { 69 | throw new DisconnectedException(); 70 | } 71 | } 72 | 73 | private boolean fragmentExists(String id) { 74 | try { 75 | return s3service.isObjectInBucket(s3bucket.getName(), id); 76 | } catch (ServiceException e) { 77 | return false; 78 | } 79 | } 80 | 81 | /* 82 | * TODO: since when is this deprecated? 83 | * 84 | * (non-Javadoc) 85 | * @see at.ac.ait.archistar.storage.StorageServer#getFragmentCount() 86 | */ 87 | @SuppressWarnings("deprecation") 88 | @Override 89 | public int getFragmentCount() throws DisconnectedException { 90 | try { 91 | return s3service.listObjects(s3bucket).length; 92 | } catch (S3ServiceException e) { 93 | throw new DisconnectedException(); 94 | } 95 | } 96 | 97 | @Override 98 | public int connect() { 99 | try { 100 | s3service = new RestS3Service(awsCredentials); 101 | Jets3tProperties props = s3service.getJetS3tProperties(); 102 | props.setProperty("s3service.s3-endpoint-http-port", "10453"); 103 | props.setProperty("s3service.https-only", "false"); 104 | props.setProperty("s3service.s3-endpoint", "fakes3.local"); 105 | 106 | CredentialsProvider credentials = s3service.getCredentialsProvider(); 107 | s3service = new RestS3Service(awsCredentials, "test-app", credentials, props); 108 | 109 | s3bucket = s3service.getBucket(bucketId); 110 | if (s3bucket == null) { 111 | s3bucket = s3service.createBucket(bucketId); 112 | } 113 | } catch (S3ServiceException e) { 114 | return -1; 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | @Override 121 | public int disconnect() { 122 | s3bucket = null; 123 | try { 124 | s3service.shutdown(); 125 | } catch (ServiceException e) { 126 | return -1; 127 | } 128 | 129 | s3service = null; 130 | return 0; 131 | } 132 | 133 | @Override 134 | public boolean isConnected() { 135 | return s3service != null; 136 | } 137 | 138 | @Override 139 | public String getId() { 140 | return "s3://amazon/" + bucketId + "/"; 141 | } 142 | 143 | @Override 144 | public int getBFTId() { 145 | return this.internalBFTId; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/storageinterface/MemoryStorage.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.storageinterface; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.UUID; 7 | 8 | /** 9 | * provide a simple storage provide through an in-memory database (honestly this 10 | * is just a java hashmap) 11 | * 12 | * @author andy 13 | */ 14 | public class MemoryStorage implements StorageServer { 15 | 16 | private HashMap data; 17 | 18 | private boolean connected = false; 19 | 20 | private final String id; 21 | 22 | private final int internalBFTId; 23 | 24 | public MemoryStorage(int bftId) { 25 | this.id = UUID.randomUUID().toString(); 26 | this.internalBFTId = bftId; 27 | } 28 | 29 | /* memory storage does not need any connect operation */ 30 | @Override 31 | public int connect() { 32 | this.data = new HashMap<>(); 33 | this.connected = true; 34 | return 0; 35 | } 36 | 37 | @Override 38 | public int disconnect() { 39 | 40 | this.data.clear(); 41 | this.connected = false; 42 | return 0; 43 | } 44 | 45 | @Override 46 | public byte[] putBlob(String path, byte[] blob) throws DisconnectedException { 47 | 48 | if (!this.connected) { 49 | throw new DisconnectedException(); 50 | } 51 | 52 | this.data.put(path, blob); 53 | return blob; 54 | } 55 | 56 | @Override 57 | public byte[] getBlob(String path) throws DisconnectedException { 58 | 59 | if (!this.connected) { 60 | throw new DisconnectedException(); 61 | } 62 | 63 | if (!this.data.containsKey(path)) { 64 | return new byte[0]; 65 | } 66 | 67 | return this.data.get(path); 68 | } 69 | 70 | @Override 71 | public boolean isConnected() { 72 | return this.connected; 73 | } 74 | 75 | @Override 76 | public int getFragmentCount() { 77 | return this.data.size(); 78 | } 79 | 80 | @Override 81 | public String getId() { 82 | return id; 83 | } 84 | 85 | @Override 86 | public int getBFTId() { 87 | return this.internalBFTId; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/backendserver/storageinterface/StorageServer.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.backendserver.storageinterface; 2 | 3 | import at.ac.ait.archistar.backendserver.ExecutionHandler; 4 | import java.io.Serializable; 5 | 6 | /** 7 | * Interface to a storage server. 8 | * 9 | * @author andy 10 | */ 11 | public interface StorageServer extends ExecutionHandler { 12 | 13 | /** 14 | * returns stored fragment count -- used for debug purposes 15 | * 16 | * @return the stored fragment count 17 | * @throws DisconnectedException 18 | */ 19 | int getFragmentCount() throws DisconnectedException; 20 | 21 | /** 22 | * connect to storage backend 23 | * 24 | * @return 0 if there was no error 25 | */ 26 | int connect(); 27 | 28 | /** 29 | * disconnect from storage backend 30 | * 31 | * @return 0 if there was no error 32 | */ 33 | int disconnect(); 34 | 35 | /** 36 | * check if storage backend is connected 37 | * 38 | * @return true if it is connected 39 | */ 40 | boolean isConnected(); 41 | 42 | /** 43 | * return storage id 44 | * 45 | * @return the storage id 46 | */ 47 | String getId(); 48 | 49 | /** 50 | * return internal BFT id 51 | * 52 | * TODO: this feels weird (as the storage server should not know about the 53 | * BFT id, maybe this is a relict from the time when BFT itself was not 54 | * implemented as a storage server -- need to investigate further, maybe 55 | * we can remove it 56 | * 57 | * @return the internal bft id 58 | */ 59 | int getBFTId(); 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/Engine.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import static org.fest.assertions.api.Assertions.*; 7 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 8 | import at.ac.ait.archistar.engine.crypto.ArchistarSMCIntegrator; 9 | import at.ac.ait.archistar.engine.dataobjects.CustomSerializer; 10 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 11 | import at.ac.ait.archistar.engine.dataobjects.SimpleFileInterface; 12 | import at.ac.ait.archistar.engine.distributor.Distributor; 13 | import at.ac.ait.archistar.engine.distributor.ServerConfiguration; 14 | import at.ac.ait.archistar.engine.metadata.MetadataService; 15 | import at.archistar.crypto.CryptoEngine; 16 | import at.archistar.crypto.secretsharing.ReconstructionException; 17 | 18 | /** 19 | * As most Archistar instances look kinda the same this class tries to capture 20 | * most of the similarities. This should prevent Archistar clients from 21 | * reimplementing the same functionality over and over again. 22 | * 23 | * @author andy 24 | */ 25 | public class Engine implements SimpleFileInterface { 26 | 27 | /** 28 | * our server configuration 29 | */ 30 | protected final ServerConfiguration servers; 31 | 32 | /** 33 | * the distributor which will transfer requests over the BFT network to the 34 | * replicasS 35 | */ 36 | protected final Distributor distributor; 37 | 38 | /** 39 | * our naming service 40 | */ 41 | private final MetadataService metadataService; 42 | 43 | /** 44 | * cryptographic directives 45 | */ 46 | private final CryptoEngine crypto; 47 | 48 | /** 49 | * the serializer used to tranform objects into byte arrays 50 | */ 51 | private final CustomSerializer serializer; 52 | 53 | /** 54 | * Create a new Archistar engine containing the following components 55 | * 56 | * @param servers which (remote) servers should be used 57 | * @param naming which metadata serverice to use for naming 58 | * @param distributor distribution options to use for storage 59 | * @param crypto which cryptographic directives to use for security 60 | */ 61 | public Engine(ServerConfiguration servers, MetadataService naming, Distributor distributor, CryptoEngine crypto) { 62 | this.servers = servers; 63 | this.distributor = distributor; 64 | this.metadataService = naming; 65 | this.crypto = crypto; 66 | this.serializer = new CustomSerializer(); 67 | } 68 | 69 | @Override 70 | public int connect() { 71 | int serverCount = this.distributor.connectServers(); 72 | this.metadataService.connect(); 73 | return serverCount; 74 | } 75 | 76 | @Override 77 | public int disconnect() { 78 | this.metadataService.disconnect(); 79 | this.distributor.disconnectServers(); 80 | return 0; 81 | } 82 | 83 | @Override 84 | public synchronized FSObject getObject(String path) throws ReconstructionException { 85 | 86 | Set fragments = this.metadataService.getDistributionFor(path); 87 | 88 | assertThat(fragments).hasSize(servers.getOnlineStorageServerCount()); 89 | boolean result = this.distributor.getFragmentSet(fragments); 90 | 91 | // TODO: just assert that nothing went wrong.. 92 | assertThat(result).isEqualTo(true); 93 | byte[] decrypted = ArchistarSMCIntegrator.decrypt(this.crypto, fragments); 94 | return this.serializer.deserialize(decrypted); 95 | } 96 | 97 | @Override 98 | public synchronized boolean putObject(FSObject obj) { 99 | assertThat(obj).isNotNull(); 100 | assertThat(metadataService).isNotNull(); 101 | 102 | Set fragments = this.metadataService.getDistributionFor(obj.getPath()); 103 | 104 | byte[] serialized = this.serializer.serialize(obj); 105 | 106 | // encrypt the fragments 107 | Set encryptedData = ArchistarSMCIntegrator.encrypt(this.crypto, serialized, fragments); 108 | 109 | return this.distributor.putFragmentSet(encryptedData); 110 | } 111 | 112 | @Override 113 | public synchronized Map statObject(String path) { 114 | return metadataService.stat(path); 115 | } 116 | 117 | @Override 118 | public synchronized int deleteObject(FSObject obj) { 119 | return metadataService.delete(obj); 120 | } 121 | 122 | @Override 123 | public synchronized Set listObjects(String path) { 124 | return this.metadataService.list(path); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/TestEngine.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine; 2 | 3 | import at.ac.ait.archistar.engine.distributor.Distributor; 4 | import at.ac.ait.archistar.engine.distributor.ServerConfiguration; 5 | import at.ac.ait.archistar.engine.metadata.MetadataService; 6 | import at.archistar.crypto.CryptoEngine; 7 | 8 | /** 9 | * This engine adds some features needed for testing. 10 | * 11 | * Mostly added to not further mess up the Engine code 12 | * 13 | * @author andy 14 | */ 15 | public class TestEngine extends Engine { 16 | 17 | public TestEngine(ServerConfiguration servers, MetadataService naming, Distributor distributor, CryptoEngine crypto) { 18 | super(servers, naming, distributor, crypto); 19 | } 20 | 21 | public int getNumberOfServers() { 22 | return servers.getOnlineStorageServerCount(); 23 | } 24 | 25 | public Distributor getDistributor() { 26 | return this.distributor; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/crypto/ArchistarSMCIntegrator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package at.ac.ait.archistar.engine.crypto; 7 | 8 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 9 | import at.archistar.crypto.CryptoEngine; 10 | import at.archistar.crypto.data.InvalidParametersException; 11 | import at.archistar.crypto.data.Share; 12 | import at.archistar.crypto.data.ShareFactory; 13 | import at.archistar.crypto.secretsharing.ReconstructionException; 14 | import at.archistar.crypto.secretsharing.WeakSecurityException; 15 | import java.io.IOException; 16 | import java.util.Arrays; 17 | import java.util.Set; 18 | import static org.fest.assertions.api.Assertions.assertThat; 19 | 20 | /** 21 | * 22 | * @author andy 23 | */ 24 | public class ArchistarSMCIntegrator { 25 | /** 26 | * split and encrypt data into fragments 27 | * 28 | * @param data the original data 29 | * @param fragments collection of expected fragments, the cryptoengine 30 | * should fill the existing fragments with the encrypted data 31 | * @return set of fragments containing encrypted data 32 | */ 33 | public static Set encrypt(CryptoEngine engine, byte[] data, Set fragments) { 34 | try { 35 | Share[] shares = engine.share(data); 36 | Fragment[] fs = fragments.toArray(new Fragment[fragments.size()]); 37 | assertThat(fs.length == shares.length); 38 | assertThat(fragments.size() == 4); 39 | 40 | for (int i = 0; i < fs.length; i++) { 41 | 42 | Fragment f = fs[i]; 43 | 44 | byte[] binData = shares[i].serialize(); 45 | assert (binData != null); 46 | assert (binData.length != 0); 47 | 48 | f.setData(binData); 49 | f.setEncryptionScheme(Fragment.EncryptionScheme.SHAMIR); 50 | } 51 | } catch (IOException e) { 52 | assert (false); 53 | } 54 | return fragments; 55 | 56 | } 57 | 58 | /** 59 | * decrypt collection of fragments into an Object 60 | * @param input set of encrypted fragments 61 | * @return origin (decrypted) user data 62 | */ 63 | public static byte[] decrypt(CryptoEngine engine, Set input) throws ReconstructionException { 64 | Share[] shares = new Share[input.size()]; 65 | 66 | int i = 0; 67 | for (Fragment f : input) { 68 | if (f.getData() != null && f.getData().length != 0) { 69 | try { 70 | shares[i++] = ShareFactory.deserialize(f.getData()); 71 | } catch (InvalidParametersException ex) { 72 | assert(false); 73 | } 74 | } 75 | } 76 | 77 | if (i == 0) { 78 | return new byte[0]; 79 | } else if (i != input.size()) { 80 | shares = Arrays.copyOf(shares, i); 81 | } 82 | 83 | return engine.reconstruct(shares); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/crypto/PseudoMirrorCryptoEngine.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.crypto; 2 | 3 | import java.util.Arrays; 4 | 5 | import at.archistar.crypto.CryptoEngine; 6 | import at.archistar.crypto.data.InvalidParametersException; 7 | import at.archistar.crypto.data.Share; 8 | import static at.archistar.crypto.data.Share.ShareType.SHAMIR_PSS; 9 | import at.archistar.crypto.data.ShareFactory; 10 | import at.archistar.crypto.secretsharing.ReconstructionException; 11 | import at.archistar.crypto.secretsharing.WeakSecurityException; 12 | import java.util.HashMap; 13 | import java.util.logging.Level; 14 | import java.util.logging.Logger; 15 | 16 | /** 17 | * This just takes a user-supplied FSObject, serializes it (using 18 | * CustomSerializer) and fills in duplicates of the serialized data into all 19 | * fragments. 20 | * 21 | * @author Andreas Happe 22 | * 23 | */ 24 | public class PseudoMirrorCryptoEngine implements CryptoEngine { 25 | 26 | private final int n; 27 | 28 | public PseudoMirrorCryptoEngine(int n) { 29 | this.n = n; 30 | } 31 | 32 | /** 33 | * checks if data within all fragments is the same and returns the 34 | * encapsulated data 35 | */ 36 | @Override 37 | public byte[] reconstruct(Share[] shares) throws ReconstructionException { 38 | byte[] reference = null; 39 | boolean first = true; 40 | 41 | for (Share f : shares) { 42 | if (first) { 43 | /* initialize on first access */ 44 | reference = f.getYValues(); 45 | } else { 46 | if (!Arrays.equals(reference, f.getYValues())) { 47 | throw new ReconstructionException(); 48 | } 49 | } 50 | } 51 | 52 | return reference; 53 | } 54 | 55 | /** 56 | * this sets data for each fragment -- no encryption whatsoever, pure 57 | * duplication 58 | */ 59 | @Override 60 | public Share[] share(byte[] data) { 61 | 62 | Share[] sshares = new Share[n]; 63 | 64 | for (int i = 0; i < n; i++) { 65 | try { 66 | sshares[i] = ShareFactory.create(SHAMIR_PSS, (byte) (i+1), data, new HashMap()); 67 | } catch (InvalidParametersException ex) { 68 | Logger.getLogger(PseudoMirrorCryptoEngine.class.getName()).log(Level.SEVERE, null, ex); 69 | assert(false); 70 | } 71 | } 72 | return sshares; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/dataobjects/CustomSerializer.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.dataobjects; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | 9 | /** 10 | * convert incoming user file objects into byte arrays (and vice versa) 11 | * 12 | * @author andy 13 | */ 14 | public class CustomSerializer implements Serializer { 15 | 16 | @Override 17 | public byte[] serialize(FSObject input) { 18 | 19 | try { 20 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 21 | try (ObjectOutputStream out = new ObjectOutputStream(bos)) { 22 | out.writeObject(input); 23 | } 24 | 25 | return bos.toByteArray(); 26 | } catch (IOException e) { 27 | return new byte[0]; 28 | } 29 | } 30 | 31 | @Override 32 | public FSObject deserialize(byte[] data) { 33 | 34 | if (data == null) { 35 | return null; 36 | } 37 | 38 | try (ObjectInputStream reader = new ObjectInputStream(new ByteArrayInputStream(data))){ 39 | return (FSObject) reader.readObject(); 40 | } catch (IOException | ClassNotFoundException e) { 41 | assert(false); 42 | } 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/dataobjects/FSObject.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.dataobjects; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * this is the base interface for all user-supplied data fragments 7 | */ 8 | public interface FSObject { 9 | 10 | Map getMetadata(); 11 | 12 | String setMetaData(String key, String value); 13 | 14 | String getPath(); 15 | }; 16 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/dataobjects/Serializer.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.dataobjects; 2 | 3 | /** 4 | * convert incoming user file objects into byte arrays (and vice versa) 5 | * 6 | * @author andy 7 | */ 8 | public interface Serializer { 9 | 10 | public byte[] serialize(FSObject input); 11 | 12 | public FSObject deserialize(byte[] data); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/dataobjects/SimpleFile.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.dataobjects; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import java.io.Serializable; 5 | import java.util.Map; 6 | 7 | /** 8 | * Provide a simple FSFile implementation for the prototype 9 | * 10 | * @author andy 11 | */ 12 | public class SimpleFile implements Serializable, FSObject { 13 | 14 | private static final long serialVersionUID = 961269599570217856L; 15 | 16 | private final String path; 17 | 18 | private final byte[] data; 19 | 20 | private final Map metadata; 21 | 22 | public SimpleFile(String path, byte[] data, Map metadata) { 23 | this.path = path; 24 | this.data = data.clone(); 25 | this.metadata = metadata; 26 | } 27 | 28 | @Override 29 | public Map getMetadata() { 30 | return this.metadata; 31 | } 32 | 33 | @Override 34 | public String setMetaData(String key, String value) { 35 | return this.metadata.put(key, value); 36 | } 37 | 38 | @SuppressFBWarnings("EI_EXPOSE_REP") 39 | public byte[] getData() { 40 | return this.data; 41 | } 42 | 43 | @Override 44 | public String getPath() { 45 | return this.path; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/dataobjects/SimpleFileInterface.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.dataobjects; 2 | 3 | import at.archistar.crypto.secretsharing.ReconstructionException; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | /** 8 | * this is the end-user API interface that Archistar implementations should use 9 | * to interact with the Archistar stack. 10 | * 11 | * @author andy 12 | */ 13 | public interface SimpleFileInterface { 14 | 15 | /** 16 | * connect to all servers 17 | */ 18 | int connect(); 19 | 20 | /** 21 | * disconnect from all servers 22 | */ 23 | int disconnect(); 24 | 25 | /** 26 | * retrieve some object from the servers 27 | */ 28 | FSObject getObject(String path) throws ReconstructionException; 29 | 30 | /** 31 | * put some object (file or directory) onto the servers 32 | */ 33 | boolean putObject(FSObject obj); 34 | 35 | /** 36 | * remove an object from all servers 37 | */ 38 | int deleteObject(FSObject obj); 39 | 40 | /** 41 | * retrieve meta-data for some object 42 | */ 43 | Map statObject(String path); 44 | 45 | /** 46 | * list objects according to some path criteria 47 | */ 48 | Set listObjects(String path); 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/distributor/BFTDistributor.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.distributor; 2 | 3 | import io.netty.channel.nio.NioEventLoopGroup; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 10 | import at.ac.ait.archistar.engine.messages.ReadCommand; 11 | import at.ac.ait.archistar.engine.messages.WriteCommand; 12 | import at.ac.ait.archistar.engine.serverinterface.OzymandiasClient; 13 | import at.archistar.bft.client.ClientResult; 14 | import at.archistar.bft.messages.ClientCommand; 15 | 16 | /** 17 | * ] 18 | * Implements the Distributor interface via a simple BFT protocol. For test 19 | * purposes this actually starts f+1 BFT servers on the local machine and wires 20 | * it up to a BFT network. 21 | * 22 | * @author andy 23 | */ 24 | public class BFTDistributor implements Distributor { 25 | 26 | protected OzymandiasClient client; 27 | 28 | private boolean alreadyConnected = false; 29 | 30 | private int clientSequence = 0; 31 | 32 | private final int clientId = 0; 33 | 34 | private final int f = 1; 35 | 36 | private final ServerConfiguration config; 37 | 38 | public BFTDistributor(ServerConfiguration config, NioEventLoopGroup loopGroup) { 39 | this.config = config; 40 | this.client = new OzymandiasClient(config.getBFTServerNetworkPortMap(), f, loopGroup); 41 | } 42 | 43 | @Override 44 | public boolean putFragmentSet(Set fragments) { 45 | 46 | Map msg = new HashMap(); 47 | 48 | for (Fragment f : fragments) { 49 | msg.put(f.getStorageServer().getBFTId(), new WriteCommand(clientId, clientSequence, f.getFragmentId(), f.getData())); 50 | } 51 | 52 | this.client.sendRoundtripMessage(msg); 53 | clientSequence++; 54 | 55 | return fragments.size() >= (2 * f + 1); 56 | } 57 | 58 | @Override 59 | public boolean getFragmentSet(Set fragments) { 60 | 61 | Map msg = new HashMap<>(); 62 | 63 | for (Fragment f : fragments) { 64 | msg.put(f.getStorageServer().getBFTId(), new ReadCommand(clientId, clientSequence, f.getFragmentId())); 65 | } 66 | 67 | ClientResult result = this.client.sendRoundtripMessage(msg); 68 | 69 | /* merge fragment results */ 70 | for (Fragment f : fragments) { 71 | int bftid = f.getStorageServer().getBFTId(); 72 | if (result.containsDataForServer(bftid)) { 73 | f.setData(result.getDataForServer(bftid)); 74 | f.setSynchronized(true); 75 | } else { 76 | f.setSynchronized(false); 77 | } 78 | } 79 | 80 | clientSequence++; 81 | 82 | return fragments.size() >= (2 * f + 1); 83 | } 84 | 85 | /* starts up virtual servers */ 86 | @Override 87 | public int connectServers() { 88 | 89 | if (alreadyConnected == true) { 90 | return config.getOnlineStorageServerCount(); 91 | } 92 | 93 | /* create and connect client */ 94 | try { 95 | this.client.connect(); 96 | alreadyConnected = true; 97 | } catch (Exception e) { 98 | e.printStackTrace(); 99 | assert (false); 100 | return -1; 101 | } 102 | 103 | return config.getOnlineStorageServerCount(); 104 | } 105 | 106 | @Override 107 | public int disconnectServers() { 108 | /* TODO: how to disconnect servers? */ 109 | return 0; 110 | } 111 | } -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/distributor/Distributor.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.distributor; 2 | 3 | import java.util.Set; 4 | 5 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 6 | 7 | /** 8 | * Distributor is responsible of directing the different operations to the 9 | * different servers. Possible solutions range from simple linear sequential 10 | * distributions to BFT algorithms 11 | * 12 | * Operations always perform on sets (as we seldomly want to access just a 13 | * single fragment on one server) 14 | * 15 | * @author andy 16 | */ 17 | public interface Distributor { 18 | 19 | boolean putFragmentSet(Set fragments); 20 | 21 | public boolean getFragmentSet(Set fragments); 22 | 23 | int connectServers(); 24 | 25 | int disconnectServers(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/distributor/ServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.distributor; 2 | 3 | import java.util.Collection; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 9 | 10 | /** 11 | * This contains the full server configuration. 12 | * 13 | * Later verions should be able to handle server reconfiguraiton (ie. dynamic 14 | * server management) 15 | * 16 | * @author andy 17 | */ 18 | public class ServerConfiguration { 19 | 20 | protected HashMap serverMapId = new HashMap<>(); 21 | 22 | public ServerConfiguration(Set servers) { 23 | for (StorageServer s : servers) { 24 | this.serverMapId.put(s.getId(), s); 25 | } 26 | } 27 | 28 | /** 29 | * this is needed for the BFT network 30 | * 31 | * @return a Map 32 | */ 33 | public Map getBFTServerNetworkPortMap() { 34 | 35 | HashMap serverList = new HashMap<>(); 36 | int i = 0; 37 | for (StorageServer s : this.serverMapId.values()) { 38 | serverList.put(s.getBFTId(), 20000 + i++); 39 | } 40 | 41 | return serverList; 42 | } 43 | 44 | public StorageServer getStorageServer(String serverid) { 45 | return this.serverMapId.get(serverid); 46 | } 47 | 48 | public Collection getOnlineStorageServers() { 49 | return this.serverMapId.values(); 50 | } 51 | 52 | public int getOnlineStorageServerCount() { 53 | return this.getOnlineStorageServers().size(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/distributor/TestServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.distributor; 2 | 3 | import io.netty.channel.nio.NioEventLoopGroup; 4 | 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | import at.ac.ait.archistar.backendserver.OzymandiasServer; 10 | import at.ac.ait.archistar.backendserver.storageinterface.DisconnectedException; 11 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 12 | 13 | /** 14 | * this adds support for creating/stopping test servers as well as a couple of 15 | * debug routines for writing test cases 16 | * 17 | * @author andy 18 | */ 19 | public class TestServerConfiguration extends ServerConfiguration { 20 | 21 | private HashSet servers = new HashSet<>(); 22 | 23 | private final NioEventLoopGroup loopGroup; 24 | 25 | private Thread[] replicas = new Thread[0]; 26 | 27 | public TestServerConfiguration(Set servers) { 28 | super(servers); 29 | this.loopGroup = new NioEventLoopGroup(16); 30 | } 31 | 32 | public TestServerConfiguration(Set servers, NioEventLoopGroup loopGroup) { 33 | super(servers); 34 | this.loopGroup = loopGroup; 35 | } 36 | 37 | /** 38 | * TODO: should we move the setupTestServer / teardownTestServer methods 39 | * into a subclass? 40 | */ 41 | public void setupTestServer(int f) { 42 | 43 | int i = 0; 44 | int servercount = serverMapId.size(); 45 | 46 | replicas = new Thread[servercount]; 47 | servers = new HashSet<>(); 48 | 49 | for (StorageServer s : serverMapId.values()) { 50 | s.connect(); 51 | 52 | OzymandiasServer tmp = new OzymandiasServer(s.getBFTId(), getBFTServerNetworkPortMap(), f, s, loopGroup, loopGroup); 53 | servers.add(tmp); 54 | Thread thr = new Thread(tmp); 55 | replicas[i++] = thr; 56 | thr.start(); 57 | } 58 | 59 | /* connect servers */ 60 | for (OzymandiasServer o : this.servers) { 61 | try { 62 | o.connectServers(); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | } 68 | 69 | public void teardownTestServer() { 70 | for (OzymandiasServer s : servers) { 71 | s.shutdown(); 72 | } 73 | 74 | for (Thread s : replicas) { 75 | try { 76 | s.join(); 77 | } catch (InterruptedException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | } 82 | 83 | public HashMap getStorageFragmentCounts() throws DisconnectedException { 84 | 85 | HashMap result = new HashMap<>(); 86 | 87 | for (StorageServer s : getOnlineStorageServers()) { 88 | result.put(s.getId(), s.getFragmentCount()); 89 | } 90 | 91 | return result; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/messages/ReadCommand.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.messages; 2 | 3 | import at.archistar.bft.messages.ClientFragmentCommand; 4 | 5 | /** 6 | * This command is used by a client to forward an update/write operation to the 7 | * replicas. 8 | * 9 | * @author andy 10 | */ 11 | public class ReadCommand extends ClientFragmentCommand { 12 | 13 | private static final long serialVersionUID = -6269916515655616482L; 14 | 15 | public ReadCommand(int clientId, int clientSequence, String fragmentId) { 16 | 17 | super(clientId, clientSequence, fragmentId); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return getClientSequence() + ": read"; 23 | } 24 | 25 | /* should this even happen? */ 26 | @Override 27 | public byte[] getPayload() { 28 | return new byte[0]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/messages/WriteCommand.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.messages; 2 | 3 | import at.archistar.bft.messages.ClientFragmentCommand; 4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 5 | 6 | /** 7 | * This command is used by a client to forward an update/write operation to the 8 | * replicas. 9 | * 10 | * @author andy 11 | */ 12 | public class WriteCommand extends ClientFragmentCommand { 13 | 14 | private static final long serialVersionUID = -6269916515655616482L; 15 | 16 | public WriteCommand(int clientId, int clientSequence, String fragmentId, byte[] data) { 17 | super(clientId, clientSequence, fragmentId); 18 | this.payload = data.clone(); 19 | } 20 | 21 | @SuppressFBWarnings("EI_EXPOSE_REP") 22 | public byte[] getData() { 23 | return this.payload; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return getClientSequence() + ": write"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/metadata/MetadataService.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.metadata; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 7 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 8 | 9 | /** 10 | * The Metadata-Service is responsible for all filesystem/object metadata 11 | * operations including: file listings, file versions, file deletion. 12 | * 13 | * @author andy 14 | */ 15 | public interface MetadataService { 16 | 17 | /** 18 | * connect and bootstrap to servers 19 | */ 20 | int connect(); 21 | 22 | /** 23 | * disconnect from all servers 24 | */ 25 | int disconnect(); 26 | 27 | /** 28 | * returns a matching for a given path 29 | */ 30 | Set getDistributionFor(String path); 31 | 32 | /** 33 | * makes sure that everything is synchronized 34 | */ 35 | int synchronize(); 36 | 37 | /** 38 | * List files in correspondence to a path expression 39 | */ 40 | Set list(String path); 41 | 42 | /** 43 | * removes an object from the index 44 | */ 45 | int delete(FSObject obj); 46 | 47 | /** 48 | * retrieves stat information about an object 49 | */ 50 | Map stat(String path); 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/metadata/SimpleMetadataService.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.metadata; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.Map; 11 | import java.util.Map.Entry; 12 | import java.util.Set; 13 | import java.util.UUID; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 19 | import at.ac.ait.archistar.backendserver.fragments.RemoteFragment; 20 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 21 | import at.ac.ait.archistar.engine.crypto.ArchistarSMCIntegrator; 22 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 23 | import at.ac.ait.archistar.engine.distributor.Distributor; 24 | import at.ac.ait.archistar.engine.distributor.ServerConfiguration; 25 | import at.archistar.crypto.CryptoEngine; 26 | import at.archistar.crypto.secretsharing.ReconstructionException; 27 | 28 | /** 29 | * The metadata service is responsible for storing all meta-information about 30 | * filesystem layout, versions, etc. 31 | * 32 | * @author Andreas Happe 33 | */ 34 | public class SimpleMetadataService implements MetadataService { 35 | 36 | private Map> database; 37 | 38 | private final Distributor distributor; 39 | 40 | private final ServerConfiguration servers; 41 | 42 | private final CryptoEngine crypto; 43 | 44 | private final Logger logger = LoggerFactory.getLogger(SimpleMetadataService.class); 45 | 46 | public SimpleMetadataService(ServerConfiguration servers, Distributor distributor, CryptoEngine crypto) { 47 | this.distributor = distributor; 48 | this.servers = servers; 49 | this.crypto = crypto; 50 | } 51 | 52 | private Set getNewDistributionSet() { 53 | HashSet distribution = new HashSet<>(); 54 | for (StorageServer s : this.servers.getOnlineStorageServers()) { 55 | distribution.add(new RemoteFragment(UUID.randomUUID().toString(), s)); 56 | } 57 | return distribution; 58 | } 59 | 60 | private Set getNewDistributionSet(String fragmentId) { 61 | HashSet distribution = new HashSet<>(); 62 | for (StorageServer s : this.servers.getOnlineStorageServers()) { 63 | distribution.add(new RemoteFragment(fragmentId, s)); 64 | } 65 | return distribution; 66 | } 67 | 68 | private byte[] serializeDatabase() { 69 | try { 70 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 71 | try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { 72 | oos.writeInt(database.size()); 73 | for (Entry> es : database.entrySet()) { 74 | oos.writeObject(es.getKey()); 75 | oos.writeInt(es.getValue().size()); 76 | for (Fragment f : es.getValue()) { 77 | oos.writeObject(f.getFragmentId()); 78 | oos.writeObject(f.getStorageServer().getId()); 79 | } 80 | } 81 | } 82 | return baos.toByteArray(); 83 | } catch (IOException e) { 84 | assert (false); 85 | } 86 | return new byte[0]; 87 | } 88 | 89 | @Override 90 | public int connect() { 91 | 92 | int result = distributor.connectServers(); 93 | 94 | /* get a new distribution set and set fragment-id to index */ 95 | Set index = getNewDistributionSet("index"); 96 | 97 | /* use crypto engine to retrieve data */ 98 | distributor.getFragmentSet(index); 99 | byte[] data; 100 | 101 | try { 102 | data = ArchistarSMCIntegrator.decrypt(this.crypto, index); 103 | } catch (ReconstructionException e) { 104 | logger.warn("error during decryption"); 105 | data = null; 106 | } 107 | 108 | /* now either rebuild database or create a new one */ 109 | if (data != null) { 110 | database = deserializeDatabase(data); 111 | } else { 112 | this.database = new HashMap<>(); 113 | synchronize(); 114 | } 115 | return result; 116 | } 117 | 118 | private Map> deserializeDatabase(byte[] readBlob) { 119 | 120 | HashMap> database = new HashMap<>(); 121 | 122 | try { 123 | ByteArrayInputStream door = new ByteArrayInputStream(readBlob); 124 | ObjectInputStream reader = new ObjectInputStream(door); 125 | 126 | int mappingCount = reader.readInt(); 127 | for (int i = 0; i < mappingCount; i++) { 128 | String filename = (String) reader.readObject(); 129 | int fragmentCount = reader.readInt(); 130 | HashSet map = new HashSet<>(); 131 | for (int j = 0; j < fragmentCount; j++) { 132 | String id = (String) reader.readObject(); 133 | String serverid = (String) reader.readObject(); 134 | map.add(new RemoteFragment(id, servers.getStorageServer(serverid))); 135 | } 136 | database.put(filename, map); 137 | } 138 | } catch (IOException | ClassNotFoundException e) { 139 | logger.warn("could not de-serialize database!"); 140 | } 141 | return database; 142 | } 143 | 144 | /** 145 | * clear our local cache/directory database 146 | */ 147 | @Override 148 | public int disconnect() { 149 | synchronize(); 150 | return 0; 151 | } 152 | 153 | @Override 154 | public Set getDistributionFor(String path) { 155 | 156 | Set distribution = database.get(path); 157 | 158 | /* if we have no mapping, create one */ 159 | if (distribution == null) { 160 | distribution = getNewDistributionSet(); 161 | database.put(path, distribution); 162 | synchronize(); 163 | } 164 | return distribution; 165 | } 166 | 167 | /** 168 | * as we are non-persistent we do not need any forced synchronization 169 | * 170 | * TODO: can we move that to the distributor? 171 | * 172 | */ 173 | @Override 174 | public int synchronize() { 175 | 176 | /* this should actually be a merge not a simple sync (for multi-user usage) */ 177 | Set index = getNewDistributionSet("index"); 178 | byte[] data = serializeDatabase(); 179 | 180 | ArchistarSMCIntegrator.encrypt(this.crypto, data, index); 181 | distributor.putFragmentSet(index); 182 | 183 | return 0; 184 | } 185 | 186 | @Override 187 | public int delete(FSObject obj) { 188 | 189 | if (this.database.containsKey(obj.getPath())) { 190 | this.database.remove(obj.getPath()); 191 | } 192 | 193 | synchronize(); 194 | return 0; 195 | } 196 | 197 | @Override 198 | public Map stat(String path) { 199 | 200 | if (this.database.containsKey(path)) { 201 | return new HashMap<>(); 202 | } else { 203 | return null; 204 | } 205 | } 206 | 207 | @Override 208 | public Set list(String path) { 209 | Set initialResult = this.database.keySet(); 210 | 211 | Set result = new HashSet<>(); 212 | for (String key : initialResult) { 213 | if (path != null) { 214 | if (key.startsWith(path)) { 215 | result.add(key); 216 | } 217 | } else { 218 | result.add(key); 219 | } 220 | } 221 | return result; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/serverinterface/OzymandiasClient.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.serverinterface; 2 | 3 | import at.ac.ait.archistar.backendserver.OzymandiasServer; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | 8 | import javax.net.ssl.SSLEngine; 9 | 10 | import at.ac.ait.archistar.trustmanager.SSLContextFactory; 11 | import at.archistar.bft.client.ClientResult; 12 | import at.archistar.bft.client.ResultManager; 13 | import at.archistar.bft.exceptions.InconsistentResultsException; 14 | import at.archistar.bft.messages.ClientCommand; 15 | import at.archistar.bft.messages.TransactionResult; 16 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 17 | import io.netty.bootstrap.Bootstrap; 18 | import io.netty.channel.Channel; 19 | import io.netty.channel.ChannelInitializer; 20 | import io.netty.channel.EventLoopGroup; 21 | import io.netty.channel.nio.NioEventLoopGroup; 22 | import io.netty.channel.socket.SocketChannel; 23 | import io.netty.channel.socket.nio.NioSocketChannel; 24 | import io.netty.handler.codec.serialization.ClassResolvers; 25 | import io.netty.handler.codec.serialization.ObjectDecoder; 26 | import io.netty.handler.codec.serialization.ObjectEncoder; 27 | import io.netty.handler.ssl.SslHandler; 28 | 29 | /** 30 | * this is the main interface for Clients contacting replicas 31 | * 32 | * @author andy 33 | */ 34 | public class OzymandiasClient { 35 | 36 | private final Map serverList; 37 | 38 | private final Map channelList; 39 | 40 | private int f = 1; 41 | 42 | private final EventLoopGroup group; 43 | 44 | private final ResultManager resultManager; 45 | 46 | public OzymandiasClient(Map serverList, int f, NioEventLoopGroup group) { 47 | this.serverList = serverList; 48 | this.channelList = new HashMap<>(); 49 | this.f = f; 50 | this.group = group; 51 | this.resultManager = new ResultManager(); 52 | } 53 | 54 | /** 55 | * Sends a message and waits for all replicas replies 56 | * 57 | * @param msg the message to be sent 58 | * @return 59 | */ 60 | public ClientResult sendRoundtripMessage(Map msg) { 61 | 62 | /* TODO: check if all clientIds and clientSequences are the same */ 63 | int clientId = msg.get(0).getClientId(); 64 | int clientSequence = msg.get(0).getClientSequence(); 65 | 66 | /* create a new operation wait object */ 67 | ClientResult result = this.resultManager.addClientOperation(f, clientId, clientSequence); 68 | 69 | /* asynchronously send a message to all replicas */ 70 | for (Entry e : msg.entrySet()) { 71 | channelList.get(e.getKey()).writeAndFlush(e.getValue()); 72 | } 73 | 74 | /* wait for answers */ 75 | result.waitForEnoughAnswers(); 76 | 77 | return result; 78 | } 79 | 80 | public void connect() throws Exception { 81 | for (Entry e : this.serverList.entrySet()) { 82 | int serverId = e.getKey(); 83 | int serverPort = e.getValue(); 84 | 85 | this.channelList.put(serverId, connectServer(serverPort)); 86 | } 87 | } 88 | 89 | @SuppressFBWarnings("SIC_INNER_SHOULD_BE_STATIC_ANON") 90 | private Channel connectServer(int port) throws Exception { 91 | 92 | final OzymandiasClientHandler handler = new OzymandiasClientHandler(this); 93 | 94 | Bootstrap b = new Bootstrap(); 95 | b.group(group) 96 | .channel(NioSocketChannel.class) 97 | .handler(new ChannelInitializer() { 98 | @Override 99 | public void initChannel(SocketChannel ch) throws Exception { 100 | 101 | SSLEngine engine = SSLContextFactory.getClientContext().createSSLEngine(); 102 | engine.setUseClientMode(true); 103 | 104 | ch.pipeline().addLast( 105 | new SslHandler(engine), 106 | new ObjectEncoder(), 107 | new ObjectDecoder(OzymandiasServer.maxObjectSize, ClassResolvers.cacheDisabled(null)), 108 | handler); 109 | } 110 | }); 111 | 112 | return b.connect("127.0.0.1", port).sync().channel(); 113 | } 114 | 115 | /** 116 | * adds a replica's result. This is used to determine when enough results 117 | * for determining an operations result were received 118 | * 119 | * @param clientId the result's client id 120 | * @param clientSequence the result's client sequence 121 | * @param tx the result 122 | * @throws InconsistentResultsException seems like a faulty replica did send 123 | * something unexpected 124 | */ 125 | public void addReplicaResult(int clientId, int clientSequence, TransactionResult tx) throws InconsistentResultsException { 126 | resultManager.addClientResponse(clientId, clientSequence, tx); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/serverinterface/OzymandiasClientHandler.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.serverinterface; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import at.archistar.bft.messages.ClientCommand; 7 | import at.archistar.bft.messages.TransactionResult; 8 | import io.netty.channel.ChannelHandlerContext; 9 | import io.netty.channel.SimpleChannelInboundHandler; 10 | 11 | /** 12 | * Listener for incoming replica messages. Currently only a transaction's result 13 | * is transmitted. 14 | * 15 | * Note: I wanted to integrate this into OzymandiasClient but failed due to threading 16 | * 17 | * @author andy 18 | */ 19 | public class OzymandiasClientHandler extends SimpleChannelInboundHandler { 20 | 21 | private Logger logger = LoggerFactory.getLogger(OzymandiasClientHandler.class); 22 | 23 | private final OzymandiasClient ozymandiasClient; 24 | 25 | public OzymandiasClientHandler(OzymandiasClient ozymandiasClient) { 26 | this.ozymandiasClient = ozymandiasClient; 27 | } 28 | 29 | @Override 30 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 31 | logger.warn("Unexpected exception from downstream.", cause); 32 | ctx.close(); 33 | } 34 | 35 | @Override 36 | protected void channelRead0(ChannelHandlerContext ctx, ClientCommand msg) throws Exception { 37 | logger.debug("received: {}", msg); 38 | if (msg instanceof TransactionResult) { 39 | this.ozymandiasClient.addReplicaResult(msg.getClientId(), msg.getClientSequence(), (TransactionResult) msg); 40 | } else { 41 | logger.warn("unknown command: {}", msg); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/userinterface/ArchistarS3.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.userinterface; 2 | 3 | import io.netty.channel.nio.NioEventLoopGroup; 4 | 5 | import java.io.File; 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | import org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer; 13 | import org.jboss.resteasy.spi.ResteasyDeployment; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import at.ac.ait.archistar.backendserver.storageinterface.FilesystemStorage; 18 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 19 | import at.ac.ait.archistar.engine.Engine; 20 | import at.ac.ait.archistar.engine.distributor.BFTDistributor; 21 | import at.ac.ait.archistar.engine.distributor.Distributor; 22 | import at.ac.ait.archistar.engine.distributor.TestServerConfiguration; 23 | import at.ac.ait.archistar.engine.metadata.MetadataService; 24 | import at.ac.ait.archistar.engine.metadata.SimpleMetadataService; 25 | import at.archistar.crypto.CryptoEngine; 26 | import at.archistar.crypto.RabinBenOrEngine; 27 | import at.archistar.crypto.secretsharing.WeakSecurityException; 28 | import at.archistar.crypto.random.FakeRandomSource; 29 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 30 | import java.security.NoSuchAlgorithmException; 31 | 32 | /** 33 | * this bootstrap a local S3 archistar instance containing one archistar 34 | * director with an client-side S3/HTTP/Rest interface and four backend-storage 35 | * servers utilizing filesystem based storage. 36 | * 37 | * Note: maybe we should exchange the filesystem based storage with a pure 38 | * memory based storage as this would remove the need for writable filesystems 39 | * for the test case 40 | * 41 | * @author andy 42 | */ 43 | public class ArchistarS3 { 44 | 45 | /** 46 | * @param args 47 | * @throws Exception 48 | */ 49 | public static void main(String[] args) throws Exception { 50 | NettyJaxrsServer netty; 51 | 52 | Logger logger = LoggerFactory.getLogger(ArchistarS3.class); 53 | 54 | logger.info("Starting archistar storage engine"); 55 | Engine engine = createEngine(); 56 | engine.connect(); 57 | 58 | 59 | /* setup buckets/services? */ 60 | ResteasyDeployment deployment = new ResteasyDeployment(); 61 | 62 | HashMap buckets = new HashMap<>(); 63 | buckets.put("fake_bucket", new FakeBucket(engine)); 64 | 65 | List resources = new LinkedList<>(); 66 | resources.add(new FakeRoot(buckets)); 67 | deployment.setResources(resources); 68 | 69 | List providers = new LinkedList<>(); 70 | providers.add(new RedirectorFilter()); 71 | deployment.setProviders(providers); 72 | 73 | netty = createServer(deployment); 74 | netty.start(); 75 | } 76 | 77 | @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME") 78 | private static final String BASE_PATH = "/var/spool/archistar/test-s3/"; 79 | 80 | private static void createDir(Set servers, File baseDir, int subdir) { 81 | File dir1 = new File(baseDir, Integer.toString(subdir + 1)); 82 | if (!dir1.exists()) { 83 | assert(dir1.mkdir()); 84 | } 85 | servers.add(new FilesystemStorage(subdir, dir1)); 86 | } 87 | 88 | @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME") 89 | private static Set createNewServers() { 90 | File baseDir = new File(BASE_PATH); 91 | 92 | if (!baseDir.exists()) { 93 | assert(baseDir.mkdirs()); 94 | } 95 | 96 | HashSet servers = new HashSet<>(); 97 | 98 | for (int i=0; i < 4; i++) { 99 | createDir(servers, baseDir, i); 100 | } 101 | return servers; 102 | } 103 | 104 | private static Engine createEngine() { 105 | NioEventLoopGroup loopGroup = new NioEventLoopGroup(16); 106 | 107 | TestServerConfiguration serverConfig = new TestServerConfiguration(createNewServers(), loopGroup); 108 | 109 | serverConfig.setupTestServer(1); 110 | try { 111 | CryptoEngine crypto = new RabinBenOrEngine(4, 3, new FakeRandomSource()); 112 | Distributor distributor = new BFTDistributor(serverConfig, loopGroup); 113 | MetadataService metadata = new SimpleMetadataService(serverConfig, distributor, crypto); 114 | return new Engine(serverConfig, metadata, distributor, crypto); 115 | } catch (NoSuchAlgorithmException | WeakSecurityException ex) { 116 | assert(false); 117 | } 118 | return null; 119 | } 120 | 121 | private static NettyJaxrsServer createServer(ResteasyDeployment deployment) throws Exception { 122 | NettyJaxrsServer netty = new NettyJaxrsServer(); 123 | netty.setDeployment(deployment); 124 | netty.setPort(8080); 125 | netty.setRootResourcePath(""); 126 | netty.setSecurityDomain(null); 127 | return netty; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/userinterface/FakeBucket.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.userinterface; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | 9 | import javax.ws.rs.core.Response; 10 | import javax.ws.rs.core.Response.ResponseBuilder; 11 | import javax.xml.parsers.ParserConfigurationException; 12 | 13 | import org.jboss.resteasy.util.Hex; 14 | 15 | import at.ac.ait.archistar.engine.Engine; 16 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 17 | import at.ac.ait.archistar.engine.dataobjects.SimpleFile; 18 | import at.archistar.crypto.secretsharing.ReconstructionException; 19 | 20 | /** 21 | * this is a fake bucket implementation that forwards all incoming S3 commands 22 | * to the internal Archistar engine (which in turn forwards those commands to 23 | * the replicas) 24 | * 25 | * @author andy 26 | */ 27 | public class FakeBucket { 28 | 29 | private final Engine engine; 30 | 31 | private final XmlDocumentBuilder builder; 32 | 33 | public FakeBucket(Engine engine) throws ParserConfigurationException { 34 | this.engine = engine; 35 | this.builder = new XmlDocumentBuilder(); 36 | } 37 | 38 | /* list all elements within bucket */ 39 | public String getAll(String bucketName, String delim, String prefix, int maxKeysInt) throws ReconstructionException { 40 | 41 | if (prefix != null && (prefix.equals("/") || prefix.equals(""))) { 42 | prefix = null; 43 | } 44 | 45 | HashSet results = new HashSet<>(); 46 | for (String key : this.engine.listObjects(prefix)) { 47 | FSObject obj = engine.getObject(key); 48 | 49 | if (obj instanceof SimpleFile) { 50 | results.add((SimpleFile) obj); 51 | } 52 | } 53 | 54 | return builder.stringFromDoc(builder.listElements(prefix, bucketName, maxKeysInt, results)); 55 | } 56 | 57 | public Response getById(String id) throws NoSuchAlgorithmException, ReconstructionException { 58 | 59 | FSObject obj = engine.getObject(id); 60 | if (obj != null && obj instanceof SimpleFile) { 61 | byte[] result = ((SimpleFile) obj).getData(); 62 | 63 | MessageDigest md = MessageDigest.getInstance("MD5"); 64 | byte[] thedigest = md.digest(result); 65 | return Response.accepted().entity(result).header("ETag", Hex.encodeHex(thedigest)).build(); 66 | } else { 67 | return null; 68 | } 69 | } 70 | 71 | public Response writeById(String id, String gid, String uid, String mode, String serverSideEncryption, byte[] input) throws NoSuchAlgorithmException, ReconstructionException { 72 | 73 | SimpleFile obj = new SimpleFile(id, input, new HashMap()); 74 | 75 | obj.setMetaData("gid", gid); 76 | obj.setMetaData("uid", uid); 77 | obj.setMetaData("mode", mode); 78 | 79 | engine.putObject(obj); 80 | 81 | MessageDigest md = MessageDigest.getInstance("MD5"); 82 | byte[] thedigest = md.digest(input); 83 | 84 | engine.getObject(id); 85 | 86 | return Response.accepted().header("ETag", Hex.encodeHex(thedigest)).build(); 87 | } 88 | 89 | public Response deleteById(String id) throws ReconstructionException { 90 | 91 | FSObject obj = engine.getObject(id); 92 | engine.deleteObject(obj); 93 | 94 | return Response.accepted().status(204).build(); 95 | } 96 | 97 | public Response getStatById(String id) throws ReconstructionException, NoSuchAlgorithmException { 98 | 99 | Map result = engine.statObject(id); 100 | 101 | if (result != null) { 102 | FSObject obj = engine.getObject(id); 103 | 104 | if (obj == null) { 105 | System.err.println("returning 404"); 106 | ResponseBuilder resp = Response.accepted().status(404); 107 | return resp.build(); 108 | } 109 | 110 | SimpleFile file = null; 111 | if (obj instanceof SimpleFile) { 112 | file = (SimpleFile) obj; 113 | } else { 114 | assert(false); 115 | } 116 | 117 | MessageDigest md = MessageDigest.getInstance("MD5"); 118 | byte[] thedigest = md.digest(file.getData()); 119 | String etag = Hex.encodeHex(thedigest); 120 | 121 | ResponseBuilder resp = Response.accepted().status(200); 122 | 123 | System.err.println("Content-Length is " + file.getData().length); 124 | 125 | resp.header("Content-Length", "" + file.getData().length); 126 | resp.header("x-foobar", "" + file.getData().length); 127 | resp.header("ETag", etag); 128 | 129 | Map md1 = file.getMetadata(); 130 | 131 | for (Map.Entry i : md1.entrySet()) { 132 | System.err.println("metadata: " + i.getKey() + " -> " + i.getValue()); 133 | } 134 | 135 | if (md1.get("uid") != null) { 136 | resp.header("x-amz-meta-uid", md1.get("uid").replace("\r\n", "")); 137 | } 138 | 139 | if (md1.get("gid") != null) { 140 | resp.header("x-amz-meta-gid", md1.get("gid").replace("\r\n", "")); 141 | } 142 | 143 | if (md1.get("mode") != null) { 144 | resp.header("x-amz-meta-mode", md1.get("mode").replace("\r\n", "")); 145 | } 146 | 147 | Response r = resp.build(); 148 | 149 | System.err.println("r->length: " + r.getLength()); 150 | System.err.println("returning 200"); 151 | return r; 152 | } else { 153 | System.err.println("returning 404"); 154 | return Response.accepted().status(404).build(); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/userinterface/FakeRoot.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.userinterface; 2 | 3 | import at.archistar.crypto.secretsharing.ReconstructionException; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.util.Map; 6 | 7 | import javax.ws.rs.DELETE; 8 | import javax.ws.rs.GET; 9 | import javax.ws.rs.HEAD; 10 | import javax.ws.rs.HeaderParam; 11 | import javax.ws.rs.PUT; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.PathParam; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.QueryParam; 16 | import javax.ws.rs.core.Response; 17 | import javax.xml.parsers.ParserConfigurationException; 18 | 19 | /** 20 | * this is the fake root-level of our S3 server. For now it just returns a list 21 | * of supported buckets 22 | * 23 | * @author andy 24 | */ 25 | @Path("/") 26 | public class FakeRoot { 27 | 28 | final private XmlDocumentBuilder builder; 29 | 30 | private final Map buckets; 31 | 32 | public FakeRoot(Map buckets) throws ParserConfigurationException { 33 | this.builder = new XmlDocumentBuilder(); 34 | this.buckets = buckets; 35 | } 36 | 37 | @GET 38 | @Produces("application/xml") 39 | public String getAll( 40 | @QueryParam("delimiter") String delim, 41 | @QueryParam("prefix") String prefix, 42 | @QueryParam("max-keys") int maxKeysInt, 43 | @HeaderParam("X-Bucket") String bucket) throws ReconstructionException { 44 | 45 | if (bucket.isEmpty()) { 46 | /* list all buckets */ 47 | return builder.stringFromDoc(builder.listBuckets(this.buckets)); 48 | } else if (!this.buckets.containsKey(bucket)) { 49 | return bucketNotFound(bucket); 50 | } else { 51 | /* return content of this bucket */ 52 | String tmp = this.buckets.get(bucket).getAll(bucket, delim, prefix, maxKeysInt); 53 | return tmp; 54 | } 55 | } 56 | 57 | private String bucketNotFound(String bucket) { 58 | return builder.stringFromDoc(builder.bucketNotFound(bucket)); 59 | } 60 | 61 | private String noSuchKey(String id) { 62 | return builder.stringFromDoc(builder.noSuchKey(id)); 63 | } 64 | 65 | @GET 66 | @Path("{id:.+}") 67 | @Produces("text/plain") 68 | public Response getById(@PathParam("id") String id, 69 | @HeaderParam("X-Bucket") String bucket 70 | ) throws ReconstructionException, NoSuchAlgorithmException { 71 | 72 | System.out.println("getById: bucket: " + bucket + " path: " + id); 73 | 74 | if (!this.buckets.containsKey(bucket)) { 75 | return Response.accepted().status(404).entity(bucketNotFound(bucket)).build(); 76 | } else { 77 | Response resp = this.buckets.get(bucket).getById(id); 78 | 79 | if (resp == null) { 80 | resp = Response.accepted().status(404).type("application/xml").entity(noSuchKey(id)).build(); 81 | } 82 | return resp; 83 | } 84 | } 85 | 86 | @HEAD 87 | @Path("{id:.+}") 88 | @Produces("text/plain") 89 | public Response getStatById(@PathParam("id") String id, 90 | @HeaderParam("X-Bucket") String bucket 91 | ) throws ReconstructionException, NoSuchAlgorithmException { 92 | 93 | if (!this.buckets.containsKey(bucket)) { 94 | return Response.accepted().status(404).entity(bucketNotFound(bucket)).build(); 95 | } else { 96 | return this.buckets.get(bucket).getStatById(id); 97 | } 98 | } 99 | 100 | @PUT 101 | @Path("{id:.+}") 102 | @Produces("text/plain") 103 | public Response writeById(@PathParam("id") String id, 104 | @HeaderParam("x-amz-server-side-encryption") String serverSideEncryption, 105 | @HeaderParam("x-amz-meta-gid") String gid, 106 | @HeaderParam("x-amz-meta-uid") String uid, 107 | @HeaderParam("x-amz-meta-mode") String mode, 108 | @HeaderParam("X-Bucket") String bucket, 109 | byte[] input) throws NoSuchAlgorithmException, ReconstructionException { 110 | 111 | if (!this.buckets.containsKey(bucket)) { 112 | return Response.accepted().status(404).entity(bucketNotFound(bucket)).build(); 113 | } else { 114 | return this.buckets.get(bucket).writeById(id, gid, uid, mode, serverSideEncryption, input); 115 | } 116 | } 117 | 118 | @DELETE 119 | @Path("{id:.+}") 120 | @Produces("text/plain") 121 | public Response deleteById(@PathParam("id") String id, 122 | @HeaderParam("X-Bucket") String bucket 123 | ) throws ReconstructionException { 124 | 125 | if (!this.buckets.containsKey(bucket)) { 126 | return Response.accepted().status(404).entity(bucketNotFound(bucket)).build(); 127 | } else { 128 | return this.buckets.get(bucket).deleteById(id); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/userinterface/RedirectorFilter.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.userinterface; 2 | 3 | import java.net.URI; 4 | import java.net.URISyntaxException; 5 | import java.util.LinkedList; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | import javax.ws.rs.container.ContainerRequestContext; 10 | import javax.ws.rs.container.ContainerRequestFilter; 11 | import javax.ws.rs.container.PreMatching; 12 | import javax.ws.rs.ext.Provider; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | /** 18 | * this filter should normalize incoming requests. S3 allows two ways of 19 | * addressing incoming requests: path style and virtual style. With this filter 20 | * all requests should be normalized to using a virtual style request 21 | * 22 | * @author Andreas Happe 23 | */ 24 | @Provider 25 | @PreMatching 26 | public class RedirectorFilter implements ContainerRequestFilter { 27 | 28 | private final Logger logger = LoggerFactory.getLogger(RedirectorFilter.class); 29 | 30 | @Override 31 | public void filter(ContainerRequestContext requestContext) { 32 | 33 | /* Host header: for path style this should be s3.amazonaws.com, 34 | * for virtual-style this should be .s3.amazonaws.com 35 | */ 36 | String host = requestContext.getHeaderString("Host"); 37 | 38 | if (host.endsWith(".s3.amazonaws.com")) { 39 | /* bucket name is already set, do nothing */ 40 | } else { 41 | /* this is some invalid host -- normally this means this is a 42 | * local S3 installation -- try to extract the bucket from 43 | * the path and set it as header 44 | */ 45 | adoptBucket(requestContext); 46 | } 47 | } 48 | 49 | private void adoptBucket(ContainerRequestContext ctx) { 50 | /* extract bucket */ 51 | URI uri = ctx.getUriInfo().getRequestUri(); 52 | 53 | String path = uri.getPath(); 54 | String bucketString = ""; 55 | String bucket = ""; 56 | String query = uri.getQuery(); 57 | 58 | logger.debug("Input: " + uri.toString()); 59 | 60 | if (path.equals("/")) { 61 | bucketString = "s3.amazonaws.com"; 62 | path = "/"; 63 | } else { 64 | Pattern p = Pattern.compile("^/*([^/]+)(.*)$"); 65 | Matcher m = p.matcher(path); 66 | 67 | if (m.find() && m.groupCount() == 2) { 68 | bucket = m.group(1); 69 | path = m.group(2); 70 | bucketString = bucket.concat(".s3.amazonaws.com"); 71 | } else { 72 | throw new RuntimeException(); 73 | } 74 | } 75 | 76 | /* nothing found, try query params */ 77 | if (path != null && uri.getQuery() != null && bucket.equalsIgnoreCase("") && path.equalsIgnoreCase("/") && !uri.getQuery().isEmpty()) { 78 | 79 | StringBuilder buffer = new StringBuilder(); 80 | for (String part : uri.getQuery().split("&")) { 81 | 82 | if (buffer.length() != 0) { 83 | buffer.append("&"); 84 | } 85 | 86 | String key = part.split("=")[0]; 87 | String value = part.split("=")[1]; 88 | 89 | if (key.equalsIgnoreCase("prefix")) { 90 | Pattern p = Pattern.compile("^/*([^/]+)(.*)$"); 91 | Matcher m = p.matcher(value); 92 | 93 | if (m.find()) { 94 | bucket = m.group(1); 95 | bucketString = bucket + ".s3.amazonaws.com"; 96 | buffer.append("prefix="); 97 | buffer.append(m.group(2)); 98 | } else { 99 | buffer.append(part); 100 | } 101 | } else { 102 | buffer.append(part); 103 | } 104 | } 105 | query = buffer.toString(); 106 | } 107 | 108 | logger.debug("redirected: " + uri.toString() + " -> " + bucket + "/" + path + "?" + query); 109 | 110 | /* set Bucket */ 111 | ctx.getHeaders().add("X-Bucket", bucket); 112 | 113 | /* set Host */ 114 | LinkedList list = new LinkedList<>(); 115 | list.add(bucketString); 116 | ctx.getHeaders().put("Host", list); 117 | 118 | /* set URI */ 119 | try { 120 | URI target = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), path, query, uri.getFragment()); 121 | logger.info("redirected: " + uri.toString() + " -> " + target.toString()); 122 | ctx.setRequestUri(target); 123 | } catch (URISyntaxException e) { 124 | assert(false); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/engine/userinterface/XmlDocumentBuilder.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.engine.userinterface; 2 | 3 | import java.io.StringWriter; 4 | import java.io.Writer; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | import java.util.Set; 10 | 11 | import javax.xml.parsers.DocumentBuilder; 12 | import javax.xml.parsers.DocumentBuilderFactory; 13 | import javax.xml.parsers.ParserConfigurationException; 14 | 15 | import org.jboss.resteasy.util.Hex; 16 | import org.w3c.dom.DOMImplementation; 17 | import org.w3c.dom.Document; 18 | import org.w3c.dom.Element; 19 | import org.w3c.dom.ls.DOMImplementationLS; 20 | import org.w3c.dom.ls.LSOutput; 21 | import org.w3c.dom.ls.LSSerializer; 22 | 23 | import at.ac.ait.archistar.engine.dataobjects.SimpleFile; 24 | 25 | public class XmlDocumentBuilder { 26 | 27 | private final DocumentBuilderFactory docFactory; 28 | private final DocumentBuilder docBuilder; 29 | 30 | public XmlDocumentBuilder() throws ParserConfigurationException { 31 | this.docFactory = DocumentBuilderFactory.newInstance(); 32 | this.docBuilder = docFactory.newDocumentBuilder(); 33 | } 34 | 35 | private Element createElement(Document doc, String name, String value) { 36 | Element xmlKey = doc.createElement(name); 37 | xmlKey.setTextContent(value); 38 | return xmlKey; 39 | } 40 | 41 | public Document listElements(String prefix, String bucketName, int maxKeys, Set results) { 42 | Document doc = this.docBuilder.newDocument(); 43 | doc.setXmlVersion("1.0"); 44 | 45 | Element rootElement = doc.createElement("ListBucketResult"); 46 | rootElement.setAttribute("xmlns", "http://doc.s3.amazonaws.com/2006-03-01"); 47 | doc.appendChild(rootElement); 48 | 49 | Element name = doc.createElement("Name"); 50 | name.setTextContent(bucketName); 51 | 52 | rootElement.appendChild(name); 53 | rootElement.appendChild(doc.createElement("Prefix")); 54 | rootElement.appendChild(doc.createElement("Marker")); 55 | 56 | rootElement.appendChild(createElement(doc, "MaxKeys", "1000")); 57 | rootElement.appendChild(createElement(doc, "isTruncated", "false")); 58 | 59 | for (SimpleFile entry : results) { 60 | Element contents = doc.createElement("Contents"); 61 | 62 | String eTag = "could not compute"; 63 | 64 | try { 65 | MessageDigest md = MessageDigest.getInstance("MD5"); 66 | byte[] thedigest = md.digest(entry.getData()); 67 | eTag = Hex.encodeHex(thedigest); 68 | } catch (NoSuchAlgorithmException e) { 69 | assert(false); 70 | } 71 | 72 | contents.appendChild(createElement(doc, "Key", entry.getPath())); 73 | contents.appendChild(createElement(doc, "Size", "" + entry.getData().length)); 74 | contents.appendChild(createElement(doc, "LastModified", "2006-02-03T16:41:58.000Z")); 75 | contents.appendChild(createElement(doc, "ETag", eTag)); 76 | 77 | rootElement.appendChild(contents); 78 | } 79 | return doc; 80 | } 81 | 82 | public Document bucketNotFound(String bucket) { 83 | 84 | Document doc = this.docBuilder.newDocument(); 85 | doc.setXmlVersion("1.0"); 86 | 87 | Element rootElement = doc.createElement("Error"); 88 | rootElement.setAttribute("xmlns", "http://doc.s3.amazonaws.com/2006-03-01"); 89 | doc.appendChild(rootElement); 90 | 91 | rootElement.appendChild(createElement(doc, "Code", "NoSuchBucket")); 92 | rootElement.appendChild(createElement(doc, "Resource", bucket)); 93 | rootElement.appendChild(createElement(doc, "Resource", bucket)); 94 | 95 | return doc; 96 | } 97 | 98 | public Document listBuckets(Map bucketList) { 99 | Document doc = this.docBuilder.newDocument(); 100 | doc.setXmlVersion("1.0"); 101 | 102 | Element rootElement = doc.createElement("ListAllMyBucketsResult"); 103 | rootElement.setAttribute("xmlns", "http://doc.s3.amazonaws.com/2006-03-01"); 104 | doc.appendChild(rootElement); 105 | 106 | Element owner = doc.createElement("Owner"); 107 | Element id = doc.createElement("ID"); 108 | id.setTextContent("deadbeef"); 109 | Element name = doc.createElement("DisplayName"); 110 | name.setTextContent("Andreas Happe"); 111 | 112 | owner.appendChild(id); 113 | owner.appendChild(name); 114 | rootElement.appendChild(owner); 115 | 116 | Element buckets = doc.createElement("Buckets"); 117 | 118 | for (Entry e : bucketList.entrySet()) { 119 | 120 | Element bucket = doc.createElement("Bucket"); 121 | 122 | bucket.appendChild(createElement(doc, "Name", e.getKey())); 123 | bucket.appendChild(createElement(doc, "CreationDate", "1982-07-07T16:41:58.000Z")); 124 | 125 | buckets.appendChild(bucket); 126 | } 127 | rootElement.appendChild(buckets); 128 | 129 | return doc; 130 | 131 | } 132 | 133 | public String stringFromDoc(Document doc) { 134 | DOMImplementation impl = doc.getImplementation(); 135 | DOMImplementationLS implLS = (DOMImplementationLS) impl.getFeature("LS", "3.0"); 136 | LSSerializer lsSerializer = implLS.createLSSerializer(); 137 | lsSerializer.getDomConfig().setParameter("format-pretty-print", true); 138 | 139 | LSOutput lsOutput = implLS.createLSOutput(); 140 | lsOutput.setEncoding("UTF-8"); 141 | Writer stringWriter = new StringWriter(); 142 | lsOutput.setCharacterStream(stringWriter); 143 | lsSerializer.write(doc, lsOutput); 144 | 145 | return stringWriter.toString(); 146 | } 147 | 148 | Document noSuchKey(String id) { 149 | Document doc = this.docBuilder.newDocument(); 150 | doc.setXmlVersion("1.0"); 151 | 152 | Element rootElement = doc.createElement("Error"); 153 | rootElement.setAttribute("xmlns", "http://doc.s3.amazonaws.com/2006-03-01"); 154 | doc.appendChild(rootElement); 155 | 156 | rootElement.appendChild(createElement(doc, "Message", "The resource you requested does not exist")); 157 | rootElement.appendChild(createElement(doc, "Code", "NoSuchKey")); 158 | rootElement.appendChild(createElement(doc, "Resource", id)); 159 | 160 | return doc; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/trustmanager/SSLContextFactory.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.trustmanager; 2 | 3 | import io.netty.handler.ssl.SslHandler; 4 | import java.io.IOException; 5 | import java.security.KeyManagementException; 6 | 7 | import java.security.KeyStore; 8 | import java.security.KeyStoreException; 9 | import java.security.NoSuchAlgorithmException; 10 | import java.security.SecureRandom; 11 | import java.security.Security; 12 | import java.security.UnrecoverableKeyException; 13 | import java.security.cert.CertificateException; 14 | 15 | import javax.net.ssl.KeyManager; 16 | import javax.net.ssl.KeyManagerFactory; 17 | import javax.net.ssl.SSLContext; 18 | import javax.net.ssl.SSLEngine; 19 | import javax.net.ssl.TrustManager; 20 | 21 | /** 22 | * Creates a bogus {@link SSLContext}. A client-side context created by this 23 | * factory accepts any certificate even if it is invalid. A server-side context 24 | * created by this factory sends a bogus certificate defined in 25 | * {@link SecureChatKeyStore}. 26 | *

27 | * You will have to create your context differently in a real world application. 28 | * 29 | *

Client Certificate Authentication

30 | * 31 | * To enable client certificate authentication: 32 | *
    33 | *
  • Enable client authentication on the server side by calling 34 | * {@link SSLEngine#setNeedClientAuth(boolean)} before creating 35 | * {@link SslHandler}.
  • 36 | *
  • When initializing an {@link SSLContext} on the client side, specify the 37 | * {@link KeyManager} that contains the client certificate as the first argument 38 | * of {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)}.
  • 39 | *
  • When initializing an {@link SSLContext} on the server side, specify the 40 | * proper {@link TrustManager} as the second argument of 41 | * {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} to 42 | * validate the client certificate.
  • 43 | *
44 | */ 45 | public final class SSLContextFactory { 46 | 47 | private static final String PROTOCOL = "TLS"; 48 | private static final SSLContext SERVER_CONTEXT; 49 | private static final SSLContext CLIENT_CONTEXT; 50 | 51 | static { 52 | String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); 53 | if (algorithm == null) { 54 | algorithm = "SunX509"; 55 | } 56 | 57 | SSLContext serverContext; 58 | SSLContext clientContext; 59 | try { 60 | KeyStore ks = KeyStore.getInstance("JKS"); 61 | ks.load(SecureKeyStore.asInputStream(), 62 | SecureKeyStore.getKeyStorePassword()); 63 | 64 | // Set up key manager factory to use our key store 65 | KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); 66 | kmf.init(ks, SecureKeyStore.getCertificatePassword()); 67 | 68 | // Initialize the SSLContext to work with our key managers. 69 | serverContext = SSLContext.getInstance(PROTOCOL); 70 | serverContext.init(kmf.getKeyManagers(), null, null); 71 | } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException e) { 72 | throw new Error( 73 | "Failed to initialize the server-side SSLContext", e); 74 | } 75 | 76 | try { 77 | clientContext = SSLContext.getInstance(PROTOCOL); 78 | clientContext.init(null, TrustManagerFactory.getTrustManagers(), null); 79 | } catch (NoSuchAlgorithmException | KeyManagementException e) { 80 | throw new Error( 81 | "Failed to initialize the client-side SSLContext", e); 82 | } 83 | 84 | SERVER_CONTEXT = serverContext; 85 | CLIENT_CONTEXT = clientContext; 86 | } 87 | 88 | public static SSLContext getServerContext() { 89 | return SERVER_CONTEXT; 90 | } 91 | 92 | public static SSLContext getClientContext() { 93 | return CLIENT_CONTEXT; 94 | } 95 | 96 | private SSLContextFactory() { 97 | // Unused 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/trustmanager/SecureKeyStore.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.trustmanager; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * A bogus key store which provides all the required information to create an 8 | * example SSL connection. 9 | * 10 | * To generate a bogus key store: 11 | *
 12 |  * keytool  -genkey -alias securechat -keysize 2048 -validity 36500
 13 |  *          -keyalg RSA -dname "CN=securechat"
 14 |  *          -keypass secret -storepass secret
 15 |  *          -keystore cert.jks
 16 |  * 
17 | */ 18 | public final class SecureKeyStore { 19 | 20 | private static final short[] DATA = { 21 | 0xfe, 0xed, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x02, 22 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 23 | 0x00, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 24 | 0x65, 0x00, 0x00, 0x01, 0x1a, 0x9f, 0x57, 0xa5, 25 | 0x27, 0x00, 0x00, 0x01, 0x9a, 0x30, 0x82, 0x01, 26 | 0x96, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 27 | 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01, 0x05, 28 | 0x00, 0x04, 0x82, 0x01, 0x82, 0x48, 0x6d, 0xcf, 29 | 0x16, 0xb5, 0x50, 0x95, 0x36, 0xbf, 0x47, 0x27, 30 | 0x50, 0x58, 0x0d, 0xa2, 0x52, 0x7e, 0x25, 0xab, 31 | 0x14, 0x1a, 0x26, 0x5e, 0x2d, 0x8a, 0x23, 0x90, 32 | 0x60, 0x7f, 0x12, 0x20, 0x56, 0xd1, 0x43, 0xa2, 33 | 0x6b, 0x47, 0x5d, 0xed, 0x9d, 0xd4, 0xe5, 0x83, 34 | 0x28, 0x89, 0xc2, 0x16, 0x4c, 0x76, 0x06, 0xad, 35 | 0x8e, 0x8c, 0x29, 0x1a, 0x9b, 0x0f, 0xdd, 0x60, 36 | 0x4b, 0xb4, 0x62, 0x82, 0x9e, 0x4a, 0x63, 0x83, 37 | 0x2e, 0xd2, 0x43, 0x78, 0xc2, 0x32, 0x1f, 0x60, 38 | 0xa9, 0x8a, 0x7f, 0x0f, 0x7c, 0xa6, 0x1d, 0xe6, 39 | 0x92, 0x9e, 0x52, 0xc7, 0x7d, 0xbb, 0x35, 0x3b, 40 | 0xaa, 0x89, 0x73, 0x4c, 0xfb, 0x99, 0x54, 0x97, 41 | 0x99, 0x28, 0x6e, 0x66, 0x5b, 0xf7, 0x9b, 0x7e, 42 | 0x6d, 0x8a, 0x2f, 0xfa, 0xc3, 0x1e, 0x71, 0xb9, 43 | 0xbd, 0x8f, 0xc5, 0x63, 0x25, 0x31, 0x20, 0x02, 44 | 0xff, 0x02, 0xf0, 0xc9, 0x2c, 0xdd, 0x3a, 0x10, 45 | 0x30, 0xab, 0xe5, 0xad, 0x3d, 0x1a, 0x82, 0x77, 46 | 0x46, 0xed, 0x03, 0x38, 0xa4, 0x73, 0x6d, 0x36, 47 | 0x36, 0x33, 0x70, 0xb2, 0x63, 0x20, 0xca, 0x03, 48 | 0xbf, 0x5a, 0xf4, 0x7c, 0x35, 0xf0, 0x63, 0x1a, 49 | 0x12, 0x33, 0x12, 0x58, 0xd9, 0xa2, 0x63, 0x6b, 50 | 0x63, 0x82, 0x41, 0x65, 0x70, 0x37, 0x4b, 0x99, 51 | 0x04, 0x9f, 0xdd, 0x5e, 0x07, 0x01, 0x95, 0x9f, 52 | 0x36, 0xe8, 0xc3, 0x66, 0x2a, 0x21, 0x69, 0x68, 53 | 0x40, 0xe6, 0xbc, 0xbb, 0x85, 0x81, 0x21, 0x13, 54 | 0xe6, 0xa4, 0xcf, 0xd3, 0x67, 0xe3, 0xfd, 0x75, 55 | 0xf0, 0xdf, 0x83, 0xe0, 0xc5, 0x36, 0x09, 0xac, 56 | 0x1b, 0xd4, 0xf7, 0x2a, 0x23, 0x57, 0x1c, 0x5c, 57 | 0x0f, 0xf4, 0xcf, 0xa2, 0xcf, 0xf5, 0xbd, 0x9c, 58 | 0x69, 0x98, 0x78, 0x3a, 0x25, 0xe4, 0xfd, 0x85, 59 | 0x11, 0xcc, 0x7d, 0xef, 0xeb, 0x74, 0x60, 0xb1, 60 | 0xb7, 0xfb, 0x1f, 0x0e, 0x62, 0xff, 0xfe, 0x09, 61 | 0x0a, 0xc3, 0x80, 0x2f, 0x10, 0x49, 0x89, 0x78, 62 | 0xd2, 0x08, 0xfa, 0x89, 0x22, 0x45, 0x91, 0x21, 63 | 0xbc, 0x90, 0x3e, 0xad, 0xb3, 0x0a, 0xb4, 0x0e, 64 | 0x1c, 0xa1, 0x93, 0x92, 0xd8, 0x72, 0x07, 0x54, 65 | 0x60, 0xe7, 0x91, 0xfc, 0xd9, 0x3c, 0xe1, 0x6f, 66 | 0x08, 0xe4, 0x56, 0xf6, 0x0b, 0xb0, 0x3c, 0x39, 67 | 0x8a, 0x2d, 0x48, 0x44, 0x28, 0x13, 0xca, 0xe9, 68 | 0xf7, 0xa3, 0xb6, 0x8a, 0x5f, 0x31, 0xa9, 0x72, 69 | 0xf2, 0xde, 0x96, 0xf2, 0xb1, 0x53, 0xb1, 0x3e, 70 | 0x24, 0x57, 0xfd, 0x18, 0x45, 0x1f, 0xc5, 0x33, 71 | 0x1b, 0xa4, 0xe8, 0x21, 0xfa, 0x0e, 0xb2, 0xb9, 72 | 0xcb, 0xc7, 0x07, 0x41, 0xdd, 0x2f, 0xb6, 0x6a, 73 | 0x23, 0x18, 0xed, 0xc1, 0xef, 0xe2, 0x4b, 0xec, 74 | 0xc9, 0xba, 0xfb, 0x46, 0x43, 0x90, 0xd7, 0xb5, 75 | 0x68, 0x28, 0x31, 0x2b, 0x8d, 0xa8, 0x51, 0x63, 76 | 0xf7, 0x53, 0x99, 0x19, 0x68, 0x85, 0x66, 0x00, 77 | 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35, 78 | 0x30, 0x39, 0x00, 0x00, 0x02, 0x3a, 0x30, 0x82, 79 | 0x02, 0x36, 0x30, 0x82, 0x01, 0xe0, 0xa0, 0x03, 80 | 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, 0xf1, 81 | 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 82 | 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 83 | 0x30, 0x81, 0xa0, 0x31, 0x0b, 0x30, 0x09, 0x06, 84 | 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 0x52, 85 | 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 86 | 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67, 87 | 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, 0x30, 88 | 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0b, 89 | 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d, 90 | 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06, 91 | 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, 0x68, 92 | 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20, 93 | 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31, 94 | 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b, 95 | 0x13, 0x0f, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 96 | 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 97 | 0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 98 | 0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 99 | 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 100 | 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 101 | 0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 102 | 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 103 | 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, 104 | 0x38, 0x30, 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 105 | 0x31, 0x33, 0x38, 0x5a, 0x18, 0x0f, 0x32, 0x31, 106 | 0x38, 0x37, 0x31, 0x31, 0x32, 0x34, 0x30, 0x35, 107 | 0x34, 0x31, 0x33, 0x38, 0x5a, 0x30, 0x81, 0xa0, 108 | 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 109 | 0x06, 0x13, 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 110 | 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 111 | 0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 112 | 0x64, 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 113 | 0x55, 0x04, 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, 114 | 0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, 115 | 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 116 | 0x0a, 0x13, 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 117 | 0x65, 0x74, 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, 118 | 0x6a, 0x65, 0x63, 0x74, 0x31, 0x18, 0x30, 0x16, 119 | 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, 0x45, 120 | 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x41, 121 | 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x31, 0x30, 122 | 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 123 | 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 124 | 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d, 125 | 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x74, 126 | 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79, 127 | 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74, 128 | 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 129 | 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 130 | 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 131 | 0x00, 0xc3, 0xe3, 0x5e, 0x41, 0xa7, 0x87, 0x11, 132 | 0x00, 0x42, 0x2a, 0xb0, 0x4b, 0xed, 0xb2, 0xe0, 133 | 0x23, 0xdb, 0xb1, 0x3d, 0x58, 0x97, 0x35, 0x60, 134 | 0x0b, 0x82, 0x59, 0xd3, 0x00, 0xea, 0xd4, 0x61, 135 | 0xb8, 0x79, 0x3f, 0xb6, 0x3c, 0x12, 0x05, 0x93, 136 | 0x2e, 0x9a, 0x59, 0x68, 0x14, 0x77, 0x3a, 0xc8, 137 | 0x50, 0x25, 0x57, 0xa4, 0x49, 0x18, 0x63, 0x41, 138 | 0xf0, 0x2d, 0x28, 0xec, 0x06, 0xfb, 0xb4, 0x9f, 139 | 0xbf, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 140 | 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 141 | 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 142 | 0x65, 0x6c, 0x30, 0x01, 0xc2, 0x8e, 0x3e, 0xcb, 143 | 0xb3, 0x77, 0x48, 0xe9, 0x66, 0x61, 0x9a, 0x40, 144 | 0x86, 0xaf, 0xf6, 0x03, 0xeb, 0xba, 0x6a, 0xf2, 145 | 0xfd, 0xe2, 0xaf, 0x36, 0x5e, 0x7b, 0xaa, 0x22, 146 | 0x04, 0xdd, 0x2c, 0x20, 0xc4, 0xfc, 0xdd, 0xd0, 147 | 0x82, 0x20, 0x1c, 0x3d, 0xd7, 0x9e, 0x5e, 0x5c, 148 | 0x92, 0x5a, 0x76, 0x71, 0x28, 0xf5, 0x07, 0x7d, 149 | 0xa2, 0x81, 0xba, 0x77, 0x9f, 0x2a, 0xd9, 0x44, 150 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x6d, 0x79, 151 | 0x6b, 0x65, 0x79, 0x00, 0x00, 0x01, 0x1a, 0x9f, 152 | 0x5b, 0x56, 0xa0, 0x00, 0x00, 0x01, 0x99, 0x30, 153 | 0x82, 0x01, 0x95, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 154 | 0x06, 0x01, 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 155 | 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x81, 0x29, 156 | 0xa8, 0xb6, 0x08, 0x0c, 0x85, 0x75, 0x3e, 0xdd, 157 | 0xb5, 0xe5, 0x1a, 0x87, 0x68, 0xd1, 0x90, 0x4b, 158 | 0x29, 0x31, 0xee, 0x90, 0xbc, 0x9d, 0x73, 0xa0, 159 | 0x3f, 0xe9, 0x0b, 0xa4, 0xef, 0x30, 0x9b, 0x36, 160 | 0x9a, 0xb2, 0x54, 0x77, 0x81, 0x07, 0x4b, 0xaa, 161 | 0xa5, 0x77, 0x98, 0xe1, 0xeb, 0xb5, 0x7c, 0x4e, 162 | 0x48, 0xd5, 0x08, 0xfc, 0x2c, 0x36, 0xe2, 0x65, 163 | 0x03, 0xac, 0xe5, 0xf3, 0x96, 0xb7, 0xd0, 0xb5, 164 | 0x3b, 0x92, 0xe4, 0x14, 0x05, 0x7a, 0x6a, 0x92, 165 | 0x56, 0xfe, 0x4e, 0xab, 0xd3, 0x0e, 0x32, 0x04, 166 | 0x22, 0x22, 0x74, 0x47, 0x7d, 0xec, 0x21, 0x99, 167 | 0x30, 0x31, 0x64, 0x46, 0x64, 0x9b, 0xc7, 0x13, 168 | 0xbf, 0xbe, 0xd0, 0x31, 0x49, 0xe7, 0x3c, 0xbf, 169 | 0xba, 0xb1, 0x20, 0xf9, 0x42, 0xf4, 0xa9, 0xa9, 170 | 0xe5, 0x13, 0x65, 0x32, 0xbf, 0x7c, 0xcc, 0x91, 171 | 0xd3, 0xfd, 0x24, 0x47, 0x0b, 0xe5, 0x53, 0xad, 172 | 0x50, 0x30, 0x56, 0xd1, 0xfa, 0x9c, 0x37, 0xa8, 173 | 0xc1, 0xce, 0xf6, 0x0b, 0x18, 0xaa, 0x7c, 0xab, 174 | 0xbd, 0x1f, 0xdf, 0xe4, 0x80, 0xb8, 0xa7, 0xe0, 175 | 0xad, 0x7d, 0x50, 0x74, 0xf1, 0x98, 0x78, 0xbc, 176 | 0x58, 0xb9, 0xc2, 0x52, 0xbe, 0xd2, 0x5b, 0x81, 177 | 0x94, 0x83, 0x8f, 0xb9, 0x4c, 0xee, 0x01, 0x2b, 178 | 0x5e, 0xc9, 0x6e, 0x9b, 0xf5, 0x63, 0x69, 0xe4, 179 | 0xd8, 0x0b, 0x47, 0xd8, 0xfd, 0xd8, 0xe0, 0xed, 180 | 0xa8, 0x27, 0x03, 0x74, 0x1e, 0x5d, 0x32, 0xe6, 181 | 0x5c, 0x63, 0xc2, 0xfb, 0x3f, 0xee, 0xb4, 0x13, 182 | 0xc6, 0x0e, 0x6e, 0x74, 0xe0, 0x22, 0xac, 0xce, 183 | 0x79, 0xf9, 0x43, 0x68, 0xc1, 0x03, 0x74, 0x2b, 184 | 0xe1, 0x18, 0xf8, 0x7f, 0x76, 0x9a, 0xea, 0x82, 185 | 0x3f, 0xc2, 0xa6, 0xa7, 0x4c, 0xfe, 0xae, 0x29, 186 | 0x3b, 0xc1, 0x10, 0x7c, 0xd5, 0x77, 0x17, 0x79, 187 | 0x5f, 0xcb, 0xad, 0x1f, 0xd8, 0xa1, 0xfd, 0x90, 188 | 0xe1, 0x6b, 0xb2, 0xef, 0xb9, 0x41, 0x26, 0xa4, 189 | 0x0b, 0x4f, 0xc6, 0x83, 0x05, 0x6f, 0xf0, 0x64, 190 | 0x40, 0xe1, 0x44, 0xc4, 0xf9, 0x40, 0x2b, 0x3b, 191 | 0x40, 0xdb, 0xaf, 0x35, 0xa4, 0x9b, 0x9f, 0xc4, 192 | 0x74, 0x07, 0xe5, 0x18, 0x60, 0xc5, 0xfe, 0x15, 193 | 0x0e, 0x3a, 0x25, 0x2a, 0x11, 0xee, 0x78, 0x2f, 194 | 0xb8, 0xd1, 0x6e, 0x4e, 0x3c, 0x0a, 0xb5, 0xb9, 195 | 0x40, 0x86, 0x27, 0x6d, 0x8f, 0x53, 0xb7, 0x77, 196 | 0x36, 0xec, 0x5d, 0xed, 0x32, 0x40, 0x43, 0x82, 197 | 0xc3, 0x52, 0x58, 0xc4, 0x26, 0x39, 0xf3, 0xb3, 198 | 0xad, 0x58, 0xab, 0xb7, 0xf7, 0x8e, 0x0e, 0xba, 199 | 0x8e, 0x78, 0x9d, 0xbf, 0x58, 0x34, 0xbd, 0x77, 200 | 0x73, 0xa6, 0x50, 0x55, 0x00, 0x60, 0x26, 0xbf, 201 | 0x6d, 0xb4, 0x98, 0x8a, 0x18, 0x83, 0x89, 0xf8, 202 | 0xcd, 0x0d, 0x49, 0x06, 0xae, 0x51, 0x6e, 0xaf, 203 | 0xbd, 0xe2, 0x07, 0x13, 0xd8, 0x64, 0xcc, 0xbf, 204 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 205 | 0x35, 0x30, 0x39, 0x00, 0x00, 0x02, 0x34, 0x30, 206 | 0x82, 0x02, 0x30, 0x30, 0x82, 0x01, 0xda, 0xa0, 207 | 0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, 208 | 0xf2, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 209 | 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 210 | 0x00, 0x30, 0x81, 0x9d, 0x31, 0x0b, 0x30, 0x09, 211 | 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 212 | 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 213 | 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 214 | 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, 215 | 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 216 | 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 217 | 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 218 | 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, 219 | 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 220 | 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 221 | 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 222 | 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 223 | 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x31, 224 | 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 225 | 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 226 | 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, 227 | 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 228 | 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 229 | 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 230 | 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, 0x38, 0x30, 231 | 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 0x35, 0x34, 232 | 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x38, 0x37, 233 | 0x31, 0x31, 0x32, 0x33, 0x30, 0x35, 0x34, 0x35, 234 | 0x34, 0x30, 0x5a, 0x30, 0x81, 0x9d, 0x31, 0x0b, 235 | 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 236 | 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 237 | 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, 238 | 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, 239 | 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 240 | 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 241 | 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, 242 | 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 243 | 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 244 | 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 245 | 0x63, 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 246 | 0x55, 0x04, 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, 247 | 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 248 | 0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 249 | 0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 250 | 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 251 | 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 252 | 0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 253 | 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 254 | 0x6e, 0x65, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06, 255 | 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 256 | 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 257 | 0x48, 0x02, 0x41, 0x00, 0x95, 0xb3, 0x47, 0x17, 258 | 0x95, 0x0f, 0x57, 0xcf, 0x66, 0x72, 0x0a, 0x7e, 259 | 0x5b, 0x54, 0xea, 0x8c, 0x6f, 0x79, 0xde, 0x94, 260 | 0xac, 0x0b, 0x5a, 0xd4, 0xd6, 0x1b, 0x58, 0x12, 261 | 0x1a, 0x16, 0x3d, 0xfe, 0xdf, 0xa5, 0x2b, 0x86, 262 | 0xbc, 0x64, 0xd4, 0x80, 0x1e, 0x3f, 0xf9, 0xe2, 263 | 0x04, 0x03, 0x79, 0x9b, 0xc1, 0x5c, 0xf0, 0xf1, 264 | 0xf3, 0xf1, 0xe3, 0xbf, 0x3f, 0xc0, 0x1f, 0xdd, 265 | 0xdb, 0xc0, 0x5b, 0x21, 0x02, 0x03, 0x01, 0x00, 266 | 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 267 | 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 268 | 0x03, 0x41, 0x00, 0x02, 0xd7, 0xdd, 0xbd, 0x0c, 269 | 0x8e, 0x21, 0x20, 0xef, 0x9e, 0x4f, 0x1f, 0xf5, 270 | 0x49, 0xf1, 0xae, 0x58, 0x9b, 0x94, 0x3a, 0x1f, 271 | 0x70, 0x33, 0xf0, 0x9b, 0xbb, 0xe9, 0xc0, 0xf3, 272 | 0x72, 0xcb, 0xde, 0xb6, 0x56, 0x72, 0xcc, 0x1c, 273 | 0xf0, 0xd6, 0x5a, 0x2a, 0xbc, 0xa1, 0x7e, 0x23, 274 | 0x83, 0xe9, 0xe7, 0xcf, 0x9e, 0xa5, 0xf9, 0xcc, 275 | 0xc2, 0x61, 0xf4, 0xdb, 0x40, 0x93, 0x1d, 0x63, 276 | 0x8a, 0x50, 0x4c, 0x11, 0x39, 0xb1, 0x91, 0xc1, 277 | 0xe6, 0x9d, 0xd9, 0x1a, 0x62, 0x1b, 0xb8, 0xd3, 278 | 0xd6, 0x9a, 0x6d, 0xb9, 0x8e, 0x15, 0x51}; 279 | 280 | public static InputStream asInputStream() { 281 | byte[] data = new byte[DATA.length]; 282 | for (int i = 0; i < data.length; i++) { 283 | data[i] = (byte) DATA[i]; 284 | } 285 | return new ByteArrayInputStream(data); 286 | } 287 | 288 | public static char[] getCertificatePassword() { 289 | return "secret".toCharArray(); 290 | } 291 | 292 | public static char[] getKeyStorePassword() { 293 | return "secret".toCharArray(); 294 | } 295 | 296 | private SecureKeyStore() { 297 | // Unused 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/main/java/at/ac/ait/archistar/trustmanager/TrustManagerFactory.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.trustmanager; 2 | 3 | import javax.net.ssl.ManagerFactoryParameters; 4 | import javax.net.ssl.TrustManager; 5 | import javax.net.ssl.TrustManagerFactorySpi; 6 | import javax.net.ssl.X509TrustManager; 7 | import java.security.InvalidAlgorithmParameterException; 8 | import java.security.KeyStore; 9 | import java.security.KeyStoreException; 10 | import java.security.cert.X509Certificate; 11 | 12 | /** 13 | * Bogus {@link TrustManagerFactorySpi} which accepts any certificate even if it 14 | * is invalid. 15 | */ 16 | public class TrustManagerFactory extends TrustManagerFactorySpi { 17 | 18 | private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() { 19 | @Override 20 | public X509Certificate[] getAcceptedIssuers() { 21 | return new X509Certificate[0]; 22 | } 23 | 24 | @Override 25 | public void checkClientTrusted(X509Certificate[] chain, String authType) { 26 | // Always trust - it is an example. 27 | // You should do something in the real world. 28 | // You will reach here only if you enabled client certificate auth, 29 | // as described in SecureChatSslContextFactory. 30 | } 31 | 32 | @Override 33 | public void checkServerTrusted(X509Certificate[] chain, String authType) { 34 | // Always trust - it is an example. 35 | // You should do something in the real world. 36 | } 37 | }; 38 | 39 | public static TrustManager[] getTrustManagers() { 40 | return new TrustManager[]{DUMMY_TRUST_MANAGER}; 41 | } 42 | 43 | @Override 44 | protected TrustManager[] engineGetTrustManagers() { 45 | return getTrustManagers(); 46 | } 47 | 48 | @Override 49 | protected void engineInit(KeyStore keystore) throws KeyStoreException { 50 | // Unused 51 | } 52 | 53 | @Override 54 | protected void engineInit(ManagerFactoryParameters managerFactoryParameters) 55 | throws InvalidAlgorithmParameterException { 56 | // Unused 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/cryptoengine/MirrorTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.cryptoengine; 2 | 3 | import static org.fest.assertions.api.Assertions.*; 4 | 5 | import org.junit.BeforeClass; 6 | import org.junit.Test; 7 | 8 | import at.ac.ait.archistar.engine.crypto.PseudoMirrorCryptoEngine; 9 | import at.archistar.crypto.CryptoEngine; 10 | import at.archistar.crypto.data.Share; 11 | import at.archistar.crypto.secretsharing.ReconstructionException; 12 | import at.archistar.crypto.secretsharing.WeakSecurityException; 13 | 14 | public class MirrorTest { 15 | 16 | private static CryptoEngine cryptoEngine; 17 | private final static byte[] mockSerializedData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 18 | 19 | @BeforeClass 20 | public static void onceSetup() { 21 | // GIVEN some test data 22 | cryptoEngine = new PseudoMirrorCryptoEngine(2); 23 | } 24 | 25 | @Test 26 | public void testIfCryptoEngineProducesEnoughFragments() throws WeakSecurityException { 27 | // WHEN i encrypt some data 28 | Share shares[] = cryptoEngine.share(mockSerializedData); 29 | assertThat(shares).hasSize(2); 30 | } 31 | 32 | @Test 33 | public void testIfMirroringWorks() throws WeakSecurityException { 34 | // WHEN i encrypt data 35 | Share shares[] = cryptoEngine.share(mockSerializedData); 36 | 37 | for(Share s : shares) { 38 | assertThat(s.getYValues()).isEqualTo(mockSerializedData); 39 | } 40 | } 41 | 42 | @Test 43 | public void testIfDecryptionProducesOriginalData() throws ReconstructionException, WeakSecurityException { 44 | Share shares[] = cryptoEngine.share(mockSerializedData); 45 | 46 | byte[] result = cryptoEngine.reconstruct(shares); 47 | assertThat(result).isEqualTo(mockSerializedData); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/cryptoengine/TestSecretSharingCryptoEngine.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.cryptoengine; 2 | 3 | import static org.fest.assertions.api.Assertions.*; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | import org.junit.BeforeClass; 9 | import org.junit.Test; 10 | 11 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 12 | import at.ac.ait.archistar.backendserver.fragments.RemoteFragment; 13 | import at.ac.ait.archistar.engine.crypto.ArchistarSMCIntegrator; 14 | import at.archistar.crypto.CryptoEngine; 15 | import at.archistar.crypto.RabinBenOrEngine; 16 | import at.archistar.crypto.secretsharing.ReconstructionException; 17 | import at.archistar.crypto.secretsharing.WeakSecurityException; 18 | import at.archistar.crypto.random.FakeRandomSource; 19 | import java.security.NoSuchAlgorithmException; 20 | 21 | public class TestSecretSharingCryptoEngine { 22 | 23 | private static CryptoEngine cryptoEngine; 24 | private final static byte[] mockSerializedData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 25 | 26 | @BeforeClass 27 | public static void onceSetup() throws WeakSecurityException, NoSuchAlgorithmException { 28 | cryptoEngine = new RabinBenOrEngine(4, 3, new FakeRandomSource()); 29 | } 30 | 31 | @Test 32 | public void testIfDecryptionProducesOriginalData() throws ReconstructionException { 33 | 34 | Set distribution = new HashSet<>(); 35 | distribution.add(new RemoteFragment("frag-1")); 36 | distribution.add(new RemoteFragment("frag-2")); 37 | distribution.add(new RemoteFragment("frag-3")); 38 | distribution.add(new RemoteFragment("frag-4")); 39 | 40 | Set encrypted = ArchistarSMCIntegrator.encrypt(cryptoEngine, mockSerializedData, distribution); 41 | 42 | assertThat(encrypted.size()).isEqualTo(4); 43 | 44 | for (Fragment f : encrypted) { 45 | assertThat(f.getData()).isNotNull(); 46 | assertThat(f.getData()).isNotEmpty(); 47 | } 48 | 49 | byte[] result = ArchistarSMCIntegrator.decrypt(cryptoEngine, encrypted); 50 | assertThat(result).isNotNull().isEqualTo(mockSerializedData); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/data/CustomSerializerTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.data; 2 | 3 | import static org.fest.assertions.api.Assertions.*; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import at.ac.ait.archistar.engine.dataobjects.CustomSerializer; 12 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 13 | import at.ac.ait.archistar.engine.dataobjects.SimpleFile; 14 | 15 | public class CustomSerializerTest { 16 | 17 | private static final String testData = "datadatadata"; 18 | private static final String testFilename = "testfilename.dat"; 19 | private CustomSerializer serializer; 20 | byte[] serializedData; 21 | 22 | @Before 23 | public void prepareTestdata() { 24 | Map metadata = new HashMap<>(); 25 | metadata.put("key0", "value0"); 26 | metadata.put("key1", "value1"); 27 | 28 | byte[] testString = testData.getBytes(); 29 | 30 | SimpleFile fs = new SimpleFile(testFilename, testString, metadata); 31 | serializer = new CustomSerializer(); 32 | serializedData = serializer.serialize(fs); 33 | } 34 | 35 | @Test 36 | public void testDataWasCreated() { 37 | assertThat(serializedData).isNotNull(); 38 | } 39 | 40 | @Test 41 | public void testDeserializationOfObject() { 42 | FSObject des = serializer.deserialize(serializedData); 43 | assertThat(des).isNotNull(); 44 | } 45 | 46 | @Test 47 | public void testPathAfterDeserialization() { 48 | FSObject des = serializer.deserialize(serializedData); 49 | assertThat(des.getPath()).isEqualTo(testFilename); 50 | } 51 | 52 | @Test 53 | public void testMetadataAfterDeserialization() { 54 | FSObject des = serializer.deserialize(serializedData); 55 | assertThat(des.getMetadata()).contains(entry("key0", "value0"), entry("key1", "value1")); 56 | } 57 | 58 | @Test 59 | public void testDataAfterDeserialization() { 60 | FSObject des = serializer.deserialize(serializedData); 61 | assertThat(des).isInstanceOf(SimpleFile.class); 62 | String decoded = new String(((SimpleFile) des).getData()); 63 | assertThat(decoded).isEqualTo(testData); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/integration/AbstractIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.integration; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.UUID; 6 | 7 | import static org.fest.assertions.api.Assertions.*; 8 | 9 | import org.junit.Test; 10 | 11 | import at.ac.ait.archistar.backendserver.storageinterface.DisconnectedException; 12 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 13 | import at.ac.ait.archistar.engine.TestEngine; 14 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 15 | import at.ac.ait.archistar.engine.dataobjects.SimpleFile; 16 | import at.ac.ait.archistar.engine.distributor.TestServerConfiguration; 17 | import at.archistar.crypto.secretsharing.ReconstructionException; 18 | 19 | public abstract class AbstractIntegrationTest { 20 | 21 | protected final static byte[] testData = {65, 66, 67, 68, 69, 70, 71, 72, 73, 74}; 22 | 23 | protected Map servers; 24 | 25 | protected static TestEngine engine; 26 | 27 | protected static TestServerConfiguration serverConfig; 28 | 29 | protected String randomTestFilename() { 30 | return UUID.randomUUID().toString(); 31 | } 32 | 33 | @Test 34 | public void testConnect() { 35 | assertThat(engine.connect()).isEqualTo(engine.getNumberOfServers()); 36 | } 37 | 38 | @Test 39 | public void testStoreOperation() { 40 | engine.connect(); 41 | 42 | SimpleFile testObject = new SimpleFile(randomTestFilename(), testData, new HashMap()); 43 | 44 | try { 45 | /* get initial fragment count */ 46 | HashMap fragCount = serverConfig.getStorageFragmentCounts(); 47 | 48 | /* add one fragment per storage server */ 49 | assertThat(engine.putObject(testObject)).isEqualTo(true); 50 | 51 | /* expect the operation to be executed at (at least) f+1 nodes. The other (3f+1)-(f+1) 52 | * nodes might still need longer to perform the operation or might be in error 53 | * 54 | * TODO: shouldn't this be 2f+1? 55 | */ 56 | int increaseCount = 0; 57 | for (Map.Entry m : serverConfig.getStorageFragmentCounts().entrySet()) { 58 | int oldValue = fragCount.get(m.getKey()); 59 | 60 | /* if index was newly created count goes from 0 -> 2, otherweise from n to n+1 */ 61 | if (m.getValue() == (oldValue + 1) || m.getValue() == 2) { 62 | increaseCount++; 63 | } else { 64 | if (m.getValue() != 0) { 65 | fail("count wasnt old_count+1 " + m.getValue() + " vs " + oldValue); 66 | } 67 | } 68 | } 69 | assertThat(increaseCount).isGreaterThanOrEqualTo(2); 70 | } catch (DisconnectedException e) { 71 | fail("error while retrieving storage fragment count", e); 72 | } 73 | } 74 | 75 | @Test 76 | public void testStoreAndRetrieveOperation() throws ReconstructionException { 77 | SimpleFile testObject = new SimpleFile(randomTestFilename(), testData, new HashMap()); 78 | String path = testObject.getPath(); 79 | 80 | engine.connect(); 81 | assertThat(engine.putObject(testObject)).isEqualTo(true); 82 | assertThat(testObject.getPath()).isEqualTo(path); 83 | 84 | FSObject retrObject = engine.getObject(path); 85 | assertThat(retrObject).isNotNull().isInstanceOf(SimpleFile.class); 86 | assertThat(path).isEqualTo(retrObject.getPath()); 87 | assertThat(((SimpleFile) retrObject).getData()).isEqualTo(testData); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/integration/BftTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.integration; 2 | 3 | import io.netty.channel.nio.NioEventLoopGroup; 4 | 5 | import java.util.HashSet; 6 | 7 | import org.junit.AfterClass; 8 | import org.junit.BeforeClass; 9 | 10 | import at.ac.ait.archistar.backendserver.storageinterface.MemoryStorage; 11 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 12 | import at.ac.ait.archistar.engine.TestEngine; 13 | import at.ac.ait.archistar.engine.distributor.BFTDistributor; 14 | import at.ac.ait.archistar.engine.distributor.Distributor; 15 | import at.ac.ait.archistar.engine.distributor.TestServerConfiguration; 16 | import at.ac.ait.archistar.engine.metadata.MetadataService; 17 | import at.ac.ait.archistar.engine.metadata.SimpleMetadataService; 18 | import at.archistar.crypto.CryptoEngine; 19 | import at.archistar.crypto.RabinBenOrEngine; 20 | import at.archistar.crypto.secretsharing.WeakSecurityException; 21 | import at.archistar.crypto.random.FakeRandomSource; 22 | import java.security.NoSuchAlgorithmException; 23 | 24 | public class BftTest extends AbstractIntegrationTest { 25 | 26 | @BeforeClass 27 | public static void prepareBftNetwork() throws NoSuchAlgorithmException, WeakSecurityException { 28 | /* test configuration */ 29 | HashSet servers = new HashSet<>(); 30 | for (int i = 0; i < 4; i ++) { 31 | servers.add(new MemoryStorage(i)); 32 | } 33 | serverConfig = new TestServerConfiguration(servers); 34 | 35 | serverConfig.setupTestServer(1); 36 | 37 | CryptoEngine crypto = new RabinBenOrEngine(4, 3, new FakeRandomSource()); 38 | Distributor distributor = new BFTDistributor(serverConfig, new NioEventLoopGroup()); 39 | MetadataService metadata = new SimpleMetadataService(serverConfig, distributor, crypto); 40 | engine = new TestEngine(serverConfig, metadata, distributor, crypto); 41 | } 42 | 43 | @AfterClass 44 | public static void shutdownServers() { 45 | serverConfig.teardownTestServer(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/integration/EncryptedFileSystemTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.integration; 2 | 3 | import static org.fest.assertions.api.Assertions.assertThat; 4 | import static org.fest.assertions.api.Assertions.fail; 5 | 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | 8 | import java.io.File; 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | import java.util.UUID; 13 | 14 | import org.junit.AfterClass; 15 | import org.junit.BeforeClass; 16 | import org.junit.Test; 17 | 18 | import at.ac.ait.archistar.backendserver.storageinterface.FilesystemStorage; 19 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 20 | import at.ac.ait.archistar.engine.TestEngine; 21 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 22 | import at.ac.ait.archistar.engine.dataobjects.SimpleFile; 23 | import at.ac.ait.archistar.engine.distributor.BFTDistributor; 24 | import at.ac.ait.archistar.engine.distributor.Distributor; 25 | import at.ac.ait.archistar.engine.distributor.TestServerConfiguration; 26 | import at.ac.ait.archistar.engine.metadata.MetadataService; 27 | import at.ac.ait.archistar.engine.metadata.SimpleMetadataService; 28 | import at.archistar.crypto.CryptoEngine; 29 | import at.archistar.crypto.RabinBenOrEngine; 30 | import at.archistar.crypto.secretsharing.ReconstructionException; 31 | import at.archistar.crypto.secretsharing.WeakSecurityException; 32 | import at.archistar.crypto.random.FakeRandomSource; 33 | import at.archistar.crypto.random.RandomSource; 34 | import java.security.NoSuchAlgorithmException; 35 | 36 | public class EncryptedFileSystemTest extends AbstractIntegrationTest { 37 | 38 | private static Set createNewServers() { 39 | File baseDir = new File("/tmp/test-encrypted-filesystem/" + UUID.randomUUID() + "/"); 40 | baseDir.mkdirs(); 41 | 42 | File dir1 = new File(baseDir, "1"); 43 | dir1.mkdir(); 44 | File dir2 = new File(baseDir, "2"); 45 | dir2.mkdir(); 46 | File dir3 = new File(baseDir, "3"); 47 | dir3.mkdir(); 48 | File dir4 = new File(baseDir, "4"); 49 | dir4.mkdir(); 50 | 51 | HashSet servers = new HashSet<>(); 52 | servers.add(new FilesystemStorage(0, dir1)); 53 | servers.add(new FilesystemStorage(1, dir2)); 54 | servers.add(new FilesystemStorage(2, dir3)); 55 | servers.add(new FilesystemStorage(3, dir4)); 56 | return servers; 57 | } 58 | 59 | @Test 60 | public void testPersistedStoreAndRetrieveOperation() throws ReconstructionException { 61 | SimpleFile testObject = new SimpleFile(randomTestFilename(), testData, new HashMap()); 62 | String path = testObject.getPath(); 63 | 64 | // WHEN I connect engine and store a fragment 65 | engine.connect(); 66 | engine.putObject(testObject); 67 | 68 | // AND I disconnect and reconnect 69 | engine.disconnect(); 70 | 71 | assertThat(engine.connect()).isEqualTo(engine.getNumberOfServers()); 72 | 73 | // THEN the data should still be available 74 | FSObject retrObject = engine.getObject(path); 75 | assertThat(retrObject).isNotNull().isInstanceOf(SimpleFile.class); 76 | assertThat(path).isEqualTo(retrObject.getPath()); 77 | assertThat(((SimpleFile) retrObject).getData()).isEqualTo(testData); 78 | } 79 | 80 | @BeforeClass 81 | public static void prepareServer() throws WeakSecurityException, NoSuchAlgorithmException { 82 | serverConfig = new TestServerConfiguration(createNewServers()); 83 | serverConfig.setupTestServer(1); 84 | 85 | RandomSource rng = new FakeRandomSource(); 86 | CryptoEngine crypto = new RabinBenOrEngine(4, 3, rng); 87 | Distributor distributor = new BFTDistributor(serverConfig, new NioEventLoopGroup()); 88 | MetadataService metadata = new SimpleMetadataService(serverConfig, distributor, crypto); 89 | engine = new TestEngine(serverConfig, metadata, distributor, crypto); 90 | } 91 | 92 | @AfterClass 93 | public static void shutdownServers() { 94 | serverConfig.teardownTestServer(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/integration/FileSystemTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.integration; 2 | 3 | import io.netty.channel.nio.NioEventLoopGroup; 4 | 5 | import java.io.File; 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.UUID; 10 | 11 | import org.junit.AfterClass; 12 | import org.junit.BeforeClass; 13 | import org.junit.Test; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import static org.fest.assertions.api.Assertions.*; 18 | import at.ac.ait.archistar.backendserver.storageinterface.FilesystemStorage; 19 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 20 | import at.ac.ait.archistar.engine.TestEngine; 21 | import at.ac.ait.archistar.engine.crypto.PseudoMirrorCryptoEngine; 22 | import at.ac.ait.archistar.engine.dataobjects.FSObject; 23 | import at.ac.ait.archistar.engine.dataobjects.SimpleFile; 24 | import at.ac.ait.archistar.engine.distributor.BFTDistributor; 25 | import at.ac.ait.archistar.engine.distributor.Distributor; 26 | import at.ac.ait.archistar.engine.distributor.TestServerConfiguration; 27 | import at.ac.ait.archistar.engine.metadata.MetadataService; 28 | import at.ac.ait.archistar.engine.metadata.SimpleMetadataService; 29 | import at.archistar.crypto.CryptoEngine; 30 | import at.archistar.crypto.secretsharing.ReconstructionException; 31 | 32 | public class FileSystemTest extends AbstractIntegrationTest { 33 | 34 | private final Logger logger = LoggerFactory.getLogger(FileSystemTest.class); 35 | 36 | private static Set createNewServers() { 37 | File baseDir = new File("/tmp/test-filesystem/" + UUID.randomUUID() + "/"); 38 | baseDir.mkdirs(); 39 | 40 | File dir1 = new File(baseDir, "1"); 41 | dir1.mkdir(); 42 | File dir2 = new File(baseDir, "2"); 43 | dir2.mkdir(); 44 | File dir3 = new File(baseDir, "3"); 45 | dir3.mkdir(); 46 | File dir4 = new File(baseDir, "4"); 47 | dir4.mkdir(); 48 | 49 | HashSet servers = new HashSet<>(); 50 | servers.add(new FilesystemStorage(0, dir1)); 51 | servers.add(new FilesystemStorage(1, dir2)); 52 | servers.add(new FilesystemStorage(2, dir3)); 53 | servers.add(new FilesystemStorage(3, dir4)); 54 | return servers; 55 | } 56 | 57 | @BeforeClass 58 | public static void prepareServer() { 59 | serverConfig = new TestServerConfiguration(createNewServers()); 60 | serverConfig.setupTestServer(1); 61 | 62 | CryptoEngine crypto = new PseudoMirrorCryptoEngine(4); 63 | Distributor distributor = new BFTDistributor(serverConfig, new NioEventLoopGroup()); 64 | MetadataService metadata = new SimpleMetadataService(serverConfig, distributor, crypto); 65 | engine = new TestEngine(serverConfig, metadata, distributor, crypto); 66 | } 67 | 68 | @Test 69 | public void testPersistedStoreAndRetrieveOperation() throws ReconstructionException { 70 | 71 | logger.info("starting test"); 72 | SimpleFile testObject = new SimpleFile(randomTestFilename(), testData, new HashMap()); 73 | String path = testObject.getPath(); 74 | 75 | // WHEN I connect engine and store a fragment 76 | engine.connect(); 77 | engine.putObject(testObject); 78 | 79 | // AND I disconnect and reconnect 80 | engine.disconnect(); 81 | 82 | logger.info("reconnecting"); 83 | assertThat(engine.connect()).isEqualTo(engine.getNumberOfServers()); 84 | 85 | // THEN the data should still be available 86 | FSObject retrObject = engine.getObject(path); 87 | assertThat(retrObject).isNotNull().isInstanceOf(SimpleFile.class); 88 | assertThat(path).isEqualTo(retrObject.getPath()); 89 | assertThat(((SimpleFile) retrObject).getData()).isEqualTo(testData); 90 | } 91 | 92 | @AfterClass 93 | public static void shutdownServers() { 94 | serverConfig.teardownTestServer(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/integration/MemoryOnlyTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.integration; 2 | 3 | import io.netty.channel.nio.NioEventLoopGroup; 4 | 5 | import java.util.HashSet; 6 | 7 | import org.junit.AfterClass; 8 | import org.junit.BeforeClass; 9 | 10 | import at.ac.ait.archistar.backendserver.storageinterface.MemoryStorage; 11 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 12 | import at.ac.ait.archistar.engine.TestEngine; 13 | import at.ac.ait.archistar.engine.crypto.PseudoMirrorCryptoEngine; 14 | import at.ac.ait.archistar.engine.distributor.BFTDistributor; 15 | import at.ac.ait.archistar.engine.distributor.Distributor; 16 | import at.ac.ait.archistar.engine.distributor.TestServerConfiguration; 17 | import at.ac.ait.archistar.engine.metadata.MetadataService; 18 | import at.ac.ait.archistar.engine.metadata.SimpleMetadataService; 19 | import at.archistar.crypto.CryptoEngine; 20 | 21 | public class MemoryOnlyTest extends AbstractIntegrationTest { 22 | 23 | @BeforeClass 24 | public static void prepareServer() { 25 | /* test configuration */ 26 | HashSet servers = new HashSet<>(); 27 | for(int i = 0; i < 4; i++) { 28 | servers.add(new MemoryStorage(i)); 29 | } 30 | serverConfig = new TestServerConfiguration(servers); 31 | serverConfig.setupTestServer(1); 32 | 33 | CryptoEngine crypto = new PseudoMirrorCryptoEngine(4); 34 | Distributor distributor = new BFTDistributor(serverConfig, new NioEventLoopGroup()); 35 | MetadataService metadata = new SimpleMetadataService(serverConfig, distributor, crypto); 36 | engine = new TestEngine(serverConfig, metadata, distributor, crypto); 37 | } 38 | 39 | @AfterClass 40 | public static void shutdownServers() { 41 | serverConfig.teardownTestServer(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/metadata/SimpleDirectoryServiceTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.metadata; 2 | 3 | import static org.fest.assertions.api.Assertions.*; 4 | import static org.mockito.Mockito.mock; 5 | import static org.mockito.Mockito.when; 6 | 7 | import java.util.Set; 8 | import java.util.HashSet; 9 | 10 | import org.junit.BeforeClass; 11 | import org.junit.Test; 12 | 13 | import at.ac.ait.archistar.backendserver.fragments.Fragment; 14 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 15 | import at.ac.ait.archistar.engine.crypto.PseudoMirrorCryptoEngine; 16 | import at.ac.ait.archistar.engine.distributor.Distributor; 17 | import at.ac.ait.archistar.engine.distributor.ServerConfiguration; 18 | import at.ac.ait.archistar.engine.metadata.MetadataService; 19 | import at.ac.ait.archistar.engine.metadata.SimpleMetadataService; 20 | import at.archistar.crypto.CryptoEngine; 21 | 22 | public class SimpleDirectoryServiceTest { 23 | 24 | private static Distributor distributor; 25 | private static MetadataService theService; 26 | private static StorageServer server1; 27 | private static StorageServer server2; 28 | private static ServerConfiguration config; 29 | 30 | @BeforeClass 31 | public static void prepareTestData() { 32 | distributor = mock(Distributor.class); 33 | config = mock(ServerConfiguration.class); 34 | 35 | Set servers = new HashSet<>(); 36 | 37 | server1 = mock(StorageServer.class); 38 | when(server1.isConnected()).thenReturn(true); 39 | servers.add(server1); 40 | 41 | server2 = mock(StorageServer.class); 42 | when(server2.isConnected()).thenReturn(true); 43 | servers.add(server2); 44 | 45 | CryptoEngine crypto = new PseudoMirrorCryptoEngine(2); 46 | 47 | Set result = new HashSet<>(); 48 | Fragment frag1 = mock(Fragment.class); 49 | when(frag1.getStorageServer()).thenReturn(server1); 50 | when(frag1.getFragmentId()).thenReturn("fragement-1"); 51 | result.add(frag1); 52 | Fragment frag2 = mock(Fragment.class); 53 | when(frag2.getStorageServer()).thenReturn(server2); 54 | when(frag2.getFragmentId()).thenReturn("fragement-2"); 55 | result.add(frag2); 56 | 57 | when(config.getOnlineStorageServerCount()).thenReturn(2); 58 | when(config.getOnlineStorageServers()).thenReturn(servers); 59 | 60 | theService = new SimpleMetadataService(config, distributor, crypto); 61 | theService.connect(); 62 | } 63 | 64 | @Test 65 | public void testIfAllServersAreIncludedInResult() { 66 | 67 | Set result = theService.getDistributionFor("/some-test-path"); 68 | assertThat(result).hasSize(config.getOnlineStorageServerCount()); 69 | 70 | Set choosenServers = new HashSet<>(); 71 | for (Fragment f : result) { 72 | choosenServers.add(f.getStorageServer()); 73 | } 74 | assertThat(choosenServers).contains(server1, server2); 75 | } 76 | 77 | @Test 78 | public void testIfRepeatedCallsGenerateSameFragmentIds() { 79 | 80 | Set result1 = theService.getDistributionFor("/some-test-path"); 81 | Set result2 = theService.getDistributionFor("/some-test-path"); 82 | 83 | // equivalent to result1.map(&:getStorageServer()) == result2.map(&:getStorageServer()) 84 | Set fragmentIds1 = new HashSet<>(); 85 | for (Fragment f : result1) { 86 | fragmentIds1.add(f.getFragmentId()); 87 | } 88 | 89 | Set fragmentIds2 = new HashSet<>(); 90 | 91 | for (Fragment f : result2) { 92 | fragmentIds2.add(f.getFragmentId()); 93 | } 94 | 95 | assertThat(fragmentIds1).containsAll(fragmentIds2); 96 | assertThat(fragmentIds2).containsAll(fragmentIds1); 97 | } 98 | 99 | @Test 100 | public void testIfRepeatedCallsGenerateSameServers() { 101 | 102 | Set result1 = theService.getDistributionFor("/some-test-path"); 103 | Set result2 = theService.getDistributionFor("/some-test-path"); 104 | 105 | // equivalent to result1.map(&:getStorageServer()) == result2.map(&:getStorageServer()) 106 | Set servers1 = new HashSet<>(); 107 | for (Fragment f : result1) { 108 | servers1.add(f.getStorageServer()); 109 | } 110 | 111 | Set servers2 = new HashSet<>(); 112 | 113 | for (Fragment f : result2) { 114 | servers2.add(f.getStorageServer()); 115 | } 116 | assertThat(servers1).containsAll(servers2); 117 | assertThat(servers2).containsAll(servers1); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/storage/AbstractStorageTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.storage; 2 | 3 | import static org.fest.assertions.api.Assertions.*; 4 | 5 | import org.junit.Test; 6 | 7 | import at.ac.ait.archistar.backendserver.storageinterface.DisconnectedException; 8 | import at.ac.ait.archistar.backendserver.storageinterface.InvalidFragmentNameException; 9 | import at.ac.ait.archistar.backendserver.storageinterface.StorageServer; 10 | 11 | public abstract class AbstractStorageTest { 12 | 13 | protected final static byte[] testData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 14 | 15 | protected final String fragmentId = "blub"; 16 | 17 | protected StorageServer store; 18 | 19 | @Test 20 | public void testConnect() { 21 | assertThat(store.connect()).isEqualTo(0); 22 | try { 23 | // WHEN I store stuff 24 | byte[] result = store.putBlob(fragmentId, testData); 25 | // THEN the same stuff should be the result of the operation 26 | assertThat(result).isEqualTo(testData); 27 | } catch (DisconnectedException e) { 28 | fail("storage server disconnected", e); 29 | } 30 | } 31 | 32 | @Test(expected = DisconnectedException.class) 33 | public void testPutBlobDisconnected() throws InvalidFragmentNameException, DisconnectedException { 34 | store.putBlob(fragmentId, testData); 35 | } 36 | 37 | @Test 38 | public void testPutBlob() { 39 | assertThat(store.connect()).isEqualTo(0); 40 | 41 | try { 42 | assertThat(store.putBlob(fragmentId, testData)).isEqualTo(testData); 43 | assertThat(store.getBlob(fragmentId)).isEqualTo(testData); 44 | } catch (DisconnectedException e) { 45 | fail("storage server disconnected", e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/storage/FileSystemStorageTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.storage; 2 | 3 | import java.io.File; 4 | import java.util.UUID; 5 | 6 | import org.apache.commons.logging.Log; 7 | import org.apache.commons.logging.LogFactory; 8 | import org.junit.Before; 9 | 10 | import at.ac.ait.archistar.backendserver.storageinterface.FilesystemStorage; 11 | import at.ac.ait.archistar.cryptoengine.MirrorTest; 12 | 13 | public class FileSystemStorageTest extends AbstractStorageTest { 14 | 15 | @Before 16 | public void prepareData() { 17 | File tmp = new File("/tmp/storage/" + UUID.randomUUID()); 18 | if (!tmp.exists()) { 19 | assert (tmp.mkdirs()); 20 | } 21 | tmp.deleteOnExit(); 22 | 23 | Log log = LogFactory.getLog(MirrorTest.class); 24 | log.info("storing file system fragments under " + tmp.getAbsolutePath()); 25 | 26 | store = new FilesystemStorage(0, tmp); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/storage/JetS3tStorageTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.storage; 2 | 3 | import org.junit.Before; 4 | 5 | import at.ac.ait.archistar.backendserver.storageinterface.JetS3tStorage; 6 | 7 | public class JetS3tStorageTest extends AbstractStorageTest { 8 | 9 | @Before 10 | public void prepareData() { 11 | store = new JetS3tStorage(0, "xxx", "yyy", "testme.snikt.net"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/at/ac/ait/archistar/storage/MemoryStoreTest.java: -------------------------------------------------------------------------------- 1 | package at.ac.ait.archistar.storage; 2 | 3 | import org.junit.Before; 4 | 5 | import at.ac.ait.archistar.backendserver.storageinterface.MemoryStorage; 6 | 7 | public class MemoryStoreTest extends AbstractStorageTest { 8 | 9 | @Before 10 | public void prepareData() { 11 | store = new MemoryStorage(0); 12 | } 13 | } 14 | --------------------------------------------------------------------------------