// OpenVPN -- An application to securely tunnel IP networks // over a single port, with support for SSL/TLS-based // session authentication and key exchange, // packet encryption, packet authentication, and // packet compression. // // Copyright (C) 2012-2021 OpenVPN Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License Version 3 // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program in the COPYING file. // If not, see . // #include #include "test_common.h" #include "openvpn/ssl/peer_fingerprint.hpp" using namespace openvpn; namespace unittests { std::vector test_fingerprint = { 0x44, 0xF5, 0xA6, 0x4D, 0x4A, 0xCB, 0x65, 0xE1, 0x8A, 0x9F, 0x55, 0x89, 0x7F, 0x77, 0xA0, 0x79, 0xAA, 0xFB, 0xCC, 0xA1, 0x37, 0x2F, 0xD8, 0xB3, 0x47, 0xAA, 0x9D, 0xE3, 0xD0, 0x76, 0xB1, 0x44 }; TEST(PeerFingerprint, parse_config) { OptionList cfg; cfg.parse_from_config( "peer-fingerprint 01:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:44\n" "\n" "02:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:44\n" "03:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:44 \n" "\n" "peer-fingerprint 04:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:44 \n" , nullptr); cfg.update_map(); struct TestPeerFingerprints : public PeerFingerprints { TestPeerFingerprints(const OptionList& opt, std::size_t fp_size) : PeerFingerprints(opt, fp_size) {} std::size_t size() { return fingerprints_.size(); } }; TestPeerFingerprints fps(cfg, test_fingerprint.size()); ASSERT_EQ(fps.size(), 4); for (std::size_t i = 1; i <= fps.size(); ++i) { auto fingerprint = test_fingerprint; fingerprint[0] = i; PeerFingerprint fp(fingerprint); ASSERT_EQ(fps.match(fp), true); } } TEST(PeerFingerprint, malformed) { OptionList cfg; cfg.parse_from_config( "peer-fingerprint 01:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1\n" , nullptr); cfg.update_map(); EXPECT_THROW(PeerFingerprints(cfg, test_fingerprint.size()), option_error); cfg.clear(); cfg.parse_from_config( "peer-fingerprint 01:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:55:FF\n" , nullptr); cfg.update_map(); EXPECT_THROW(PeerFingerprints(cfg, test_fingerprint.size()), option_error); cfg.clear(); cfg.parse_from_config( "peer-fingerprint 101:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:55\n" , nullptr); cfg.update_map(); EXPECT_THROW(PeerFingerprints(cfg, test_fingerprint.size()), option_error); cfg.clear(); cfg.parse_from_config( "peer-fingerprint 11:F5:A6:4D:4A:1CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:55\n" , nullptr); cfg.update_map(); EXPECT_THROW(PeerFingerprints(cfg, test_fingerprint.size()), option_error); cfg.clear(); cfg.parse_from_config( "peer-fingerprint 11/F5/A6/4D/4A/CB/65/E1/8A/9F/55/89/7F/77/A0/79/AA/FB/CC/A1/37/2F/D8/B3/47/AA/9D/E3/D0/76/B1/55\n" , nullptr); cfg.update_map(); EXPECT_THROW(PeerFingerprints(cfg, test_fingerprint.size()), option_error); cfg.clear(); cfg.parse_from_config( "\n" " 02:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:44\n" "\n" , nullptr); cfg.update_map(); EXPECT_THROW(PeerFingerprints(cfg, test_fingerprint.size()), option_error); } TEST(PeerFingerprint, stringify) { std::string hex_fp("01:f5:a6:4d:4a:cb:65:e1:8a:9f:55:89:7f:77:a0:79:aa:fb:cc:a1:37:2f:d8:b3:47:aa:9d:e3:d0:76:b1:55"); PeerFingerprint fp(hex_fp, test_fingerprint.size()); ASSERT_EQ(fp.str(), hex_fp); } TEST(PeerFingerprint, match_empty) { PeerFingerprint fp(test_fingerprint); PeerFingerprints fps; ASSERT_FALSE(fps); ASSERT_EQ(fps.match(fp), false); } TEST(PeerFingerprint, match) { OptionList cfg; cfg.parse_from_config( "\n" "A4:E5:A7:1D:AA:E3:65:E1:3A:6E:45:89:80:66:A0:79:BB:E3:EC:41:34:2F:08:83:97:AA:91:33:DF:11:31:AA\n" "44:F5:A6:4D:4A:CB:65:E1:8A:9F:55:89:7F:77:A0:79:AA:FB:CC:A1:37:2F:D8:B3:47:AA:9D:E3:D0:76:B1:44\n" "\n" , nullptr); cfg.update_map(); PeerFingerprint fp(test_fingerprint); PeerFingerprints fps(cfg, test_fingerprint.size()); ASSERT_EQ(fps.match(fp), true); } TEST(PeerFingerprint, no_match) { OptionList cfg; cfg.parse_from_config( "peer-fingerprint A4:E5:A7:1D:AA:E3:65:E1:3A:6E:45:89:80:66:A0:79:BB:E3:EC:41:34:2F:08:83:97:AA:91:33:DF:11:31:AA\n" , nullptr); cfg.update_map(); PeerFingerprint fp(test_fingerprint); PeerFingerprints fps(cfg, test_fingerprint.size()); ASSERT_EQ(fps.match(fp), false); } } // namespace unittests