Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2011 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2011 The WebRTC Project Authors. All rights reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 "-----END CERTIFICATE-----\n"; | 31 "-----END CERTIFICATE-----\n"; |
| 32 | 32 |
| 33 const unsigned char kTestCertSha1[] = {0xA6, 0xC8, 0x59, 0xEA, | 33 const unsigned char kTestCertSha1[] = {0xA6, 0xC8, 0x59, 0xEA, |
| 34 0xC3, 0x7E, 0x6D, 0x33, | 34 0xC3, 0x7E, 0x6D, 0x33, |
| 35 0xCF, 0xE2, 0x69, 0x9D, | 35 0xCF, 0xE2, 0x69, 0x9D, |
| 36 0x74, 0xE6, 0xF6, 0x8A, | 36 0x74, 0xE6, 0xF6, 0x8A, |
| 37 0x9E, 0x47, 0xA7, 0xCA}; | 37 0x9E, 0x47, 0xA7, 0xCA}; |
| 38 | 38 |
| 39 class SSLIdentityTest : public testing::Test { | 39 class SSLIdentityTest : public testing::Test { |
| 40 public: | 40 public: |
| 41 SSLIdentityTest() : | 41 SSLIdentityTest() |
| 42 identity1_(), identity2_() { | 42 : identityrsa1_(), |
| 43 } | 43 identityrsa2_(), |
| 44 identityecdsa1_(), | |
| 45 identityecdsa2_() {} | |
| 44 | 46 |
| 45 ~SSLIdentityTest() { | 47 ~SSLIdentityTest() { |
| 46 } | 48 } |
| 47 | 49 |
| 48 virtual void SetUp() { | 50 virtual void SetUp() { |
| 49 identity1_.reset(SSLIdentity::Generate("test1")); | 51 identityrsa1_.reset(SSLIdentity::Generate("test1", rtc::KT_RSA)); |
| 50 identity2_.reset(SSLIdentity::Generate("test2")); | 52 identityrsa2_.reset(SSLIdentity::Generate("test2", rtc::KT_RSA)); |
| 53 identityecdsa1_.reset(SSLIdentity::Generate("test3", rtc::KT_ECDSA)); | |
| 54 identityecdsa2_.reset(SSLIdentity::Generate("test4", rtc::KT_ECDSA)); | |
| 51 | 55 |
| 52 ASSERT_TRUE(identity1_); | 56 ASSERT_TRUE(identityrsa1_); |
| 53 ASSERT_TRUE(identity2_); | 57 ASSERT_TRUE(identityrsa2_); |
| 58 ASSERT_TRUE(identityecdsa1_); | |
| 59 ASSERT_TRUE(identityecdsa2_); | |
| 54 | 60 |
| 55 test_cert_.reset( | 61 test_cert_.reset(rtc::SSLCertificate::FromPEMString(kTestCertificate)); |
| 56 rtc::SSLCertificate::FromPEMString(kTestCertificate)); | |
| 57 ASSERT_TRUE(test_cert_); | 62 ASSERT_TRUE(test_cert_); |
| 58 } | 63 } |
| 59 | 64 |
| 60 void TestGetSignatureDigestAlgorithm() { | 65 void TestGetSignatureDigestAlgorithm() { |
| 61 std::string digest_algorithm; | 66 std::string digest_algorithm; |
| 62 // Both NSSIdentity::Generate and OpenSSLIdentity::Generate are | 67 |
| 63 // hard-coded to generate RSA-SHA256 certificates. | 68 ASSERT_TRUE(identityrsa1_->certificate().GetSignatureDigestAlgorithm( |
| 64 ASSERT_TRUE(identity1_->certificate().GetSignatureDigestAlgorithm( | |
| 65 &digest_algorithm)); | |
| 66 ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); | |
| 67 ASSERT_TRUE(identity2_->certificate().GetSignatureDigestAlgorithm( | |
| 68 &digest_algorithm)); | 69 &digest_algorithm)); |
| 69 ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); | 70 ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); |
| 70 | 71 |
| 72 ASSERT_TRUE(identityrsa2_->certificate().GetSignatureDigestAlgorithm( | |
| 73 &digest_algorithm)); | |
| 74 ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); | |
| 75 | |
| 76 ASSERT_TRUE(identityecdsa1_->certificate().GetSignatureDigestAlgorithm( | |
| 77 &digest_algorithm)); | |
| 78 ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); | |
| 79 | |
| 80 ASSERT_TRUE(identityecdsa2_->certificate().GetSignatureDigestAlgorithm( | |
| 81 &digest_algorithm)); | |
| 82 ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm); | |
| 83 | |
| 71 // The test certificate has an MD5-based signature. | 84 // The test certificate has an MD5-based signature. |
| 72 ASSERT_TRUE(test_cert_->GetSignatureDigestAlgorithm(&digest_algorithm)); | 85 ASSERT_TRUE(test_cert_->GetSignatureDigestAlgorithm(&digest_algorithm)); |
| 73 ASSERT_EQ(rtc::DIGEST_MD5, digest_algorithm); | 86 ASSERT_EQ(rtc::DIGEST_MD5, digest_algorithm); |
| 74 } | 87 } |
| 75 | 88 |
| 76 void TestDigest(const std::string &algorithm, size_t expected_len, | 89 void TestDigest(const std::string &algorithm, size_t expected_len, |
| 77 const unsigned char *expected_digest = NULL) { | 90 const unsigned char *expected_digest = NULL) { |
| 78 unsigned char digest1[64]; | 91 typedef unsigned char digest_type[64]; // 64 is the largest hash used here. |
|
juberti1
2015/07/03 03:15:18
proper style for this would be DigestType
torbjorng (webrtc)
2015/07/06 10:11:55
Done.
| |
| 79 unsigned char digest1b[64]; | 92 digest_type digest1; |
| 80 unsigned char digest2[64]; | 93 digest_type digest[4]; |
| 81 size_t digest1_len; | 94 size_t digest_len; |
| 82 size_t digest1b_len; | |
| 83 size_t digest2_len; | |
| 84 bool rv; | 95 bool rv; |
| 85 | 96 |
| 86 rv = identity1_->certificate().ComputeDigest(algorithm, | 97 ASSERT_TRUE(expected_len <= 64); |
| 87 digest1, sizeof(digest1), | 98 |
| 88 &digest1_len); | 99 // Record digests of our 4 identities. |
| 100 memset(digest[0], 0, expected_len); | |
|
juberti1
2015/07/03 03:15:18
This seems like a lot of redundant code. Suggest e
torbjorng (webrtc)
2015/07/06 10:11:55
Done.
| |
| 101 rv = identityrsa1_->certificate().ComputeDigest( | |
| 102 algorithm, digest[0], sizeof(digest_type), &digest_len); | |
| 89 EXPECT_TRUE(rv); | 103 EXPECT_TRUE(rv); |
| 90 EXPECT_EQ(expected_len, digest1_len); | 104 EXPECT_EQ(expected_len, digest_len); |
| 91 | 105 |
| 92 rv = identity1_->certificate().ComputeDigest(algorithm, | 106 memset(digest[1], 0, expected_len); |
| 93 digest1b, sizeof(digest1b), | 107 rv = identityrsa2_->certificate().ComputeDigest( |
| 94 &digest1b_len); | 108 algorithm, digest[1], sizeof(digest_type), &digest_len); |
| 95 EXPECT_TRUE(rv); | 109 EXPECT_TRUE(rv); |
| 96 EXPECT_EQ(expected_len, digest1b_len); | 110 EXPECT_EQ(expected_len, digest_len); |
| 97 EXPECT_EQ(0, memcmp(digest1, digest1b, expected_len)); | |
| 98 | 111 |
| 112 memset(digest[2], 0, expected_len); | |
| 113 rv = identityecdsa1_->certificate().ComputeDigest( | |
| 114 algorithm, digest[2], sizeof(digest_type), &digest_len); | |
| 115 EXPECT_TRUE(rv); | |
| 116 EXPECT_EQ(expected_len, digest_len); | |
| 99 | 117 |
| 100 rv = identity2_->certificate().ComputeDigest(algorithm, | 118 memset(digest[3], 0, expected_len); |
| 101 digest2, sizeof(digest2), | 119 rv = identityecdsa2_->certificate().ComputeDigest( |
| 102 &digest2_len); | 120 algorithm, digest[3], sizeof(digest_type), &digest_len); |
| 103 EXPECT_TRUE(rv); | 121 EXPECT_TRUE(rv); |
| 104 EXPECT_EQ(expected_len, digest2_len); | 122 EXPECT_EQ(expected_len, digest_len); |
| 105 EXPECT_NE(0, memcmp(digest1, digest2, expected_len)); | 123 |
| 124 // Repeat digest computation for one identity as a sanity check. | |
| 125 memset(digest1, 0xff, expected_len); | |
| 126 rv = identityrsa1_->certificate().ComputeDigest( | |
| 127 algorithm, digest1, sizeof(digest_type), &digest_len); | |
| 128 EXPECT_TRUE(rv); | |
| 129 EXPECT_EQ(expected_len, digest_len); | |
| 130 EXPECT_EQ(0, memcmp(digest[0], digest1, expected_len)); | |
| 131 | |
| 132 // Sanity check that all four digests are unique. This could theoretically | |
|
juberti1
2015/07/03 03:15:18
This seems like it might not be all that valuable
torbjorng (webrtc)
2015/07/06 10:11:55
I inherited this check from the old code, I only g
| |
| 133 // fail, since SHA256 collisions have a non-zero probability. | |
| 134 for (int i = 0; i < 4; i++) { | |
| 135 for (int j = 0; j < 4; j++) { | |
| 136 if (i != j) | |
| 137 EXPECT_NE(0, memcmp(digest[i], digest[j], expected_len)); | |
| 138 } | |
| 139 } | |
| 106 | 140 |
| 107 // If we have an expected hash for the test cert, check it. | 141 // If we have an expected hash for the test cert, check it. |
| 108 if (expected_digest) { | 142 if (expected_digest) { |
|
juberti1
2015/07/03 03:15:18
This is only used in one case, so suggest factorin
torbjorng (webrtc)
2015/07/06 10:11:55
Done.
| |
| 109 unsigned char digest3[64]; | 143 digest_type digest3; |
| 110 size_t digest3_len; | 144 size_t digest3_len; |
| 111 | 145 |
| 112 rv = test_cert_->ComputeDigest(algorithm, digest3, sizeof(digest3), | 146 rv = test_cert_->ComputeDigest(algorithm, digest3, sizeof(digest3), |
| 113 &digest3_len); | 147 &digest3_len); |
| 114 EXPECT_TRUE(rv); | 148 EXPECT_TRUE(rv); |
| 115 EXPECT_EQ(expected_len, digest3_len); | 149 EXPECT_EQ(expected_len, digest3_len); |
| 116 EXPECT_EQ(0, memcmp(digest3, expected_digest, expected_len)); | 150 EXPECT_EQ(0, memcmp(digest3, expected_digest, expected_len)); |
| 117 } | 151 } |
| 118 } | 152 } |
| 119 | 153 |
| 120 private: | 154 private: |
| 121 rtc::scoped_ptr<SSLIdentity> identity1_; | 155 rtc::scoped_ptr<SSLIdentity> identityrsa1_; |
|
juberti1
2015/07/03 03:15:18
identiy_rsa1_, etc would be more readable
torbjorng (webrtc)
2015/07/06 10:11:55
Done.
| |
| 122 rtc::scoped_ptr<SSLIdentity> identity2_; | 156 rtc::scoped_ptr<SSLIdentity> identityrsa2_; |
| 157 rtc::scoped_ptr<SSLIdentity> identityecdsa1_; | |
| 158 rtc::scoped_ptr<SSLIdentity> identityecdsa2_; | |
| 123 rtc::scoped_ptr<rtc::SSLCertificate> test_cert_; | 159 rtc::scoped_ptr<rtc::SSLCertificate> test_cert_; |
| 124 }; | 160 }; |
| 125 | 161 |
| 126 TEST_F(SSLIdentityTest, DigestSHA1) { | 162 TEST_F(SSLIdentityTest, DigestSHA1) { |
| 127 TestDigest(rtc::DIGEST_SHA_1, 20, kTestCertSha1); | 163 TestDigest(rtc::DIGEST_SHA_1, 20, kTestCertSha1); |
| 128 } | 164 } |
| 129 | 165 |
| 130 // HASH_AlgSHA224 is not supported in the chromium linux build. | 166 // HASH_AlgSHA224 is not supported in the chromium linux build. |
| 131 #if SSL_USE_NSS | 167 #if SSL_USE_NSS |
| 132 TEST_F(SSLIdentityTest, DISABLED_DigestSHA224) { | 168 TEST_F(SSLIdentityTest, DISABLED_DigestSHA224) { |
| 133 #else | 169 #else |
| 134 TEST_F(SSLIdentityTest, DigestSHA224) { | 170 TEST_F(SSLIdentityTest, DigestSHA224) { |
| 135 #endif | 171 #endif |
| 136 TestDigest(rtc::DIGEST_SHA_224, 28); | 172 TestDigest(rtc::DIGEST_SHA_224, 28); |
| 137 } | 173 } |
| 138 | 174 |
| 139 TEST_F(SSLIdentityTest, DigestSHA256) { | 175 TEST_F(SSLIdentityTest, DigestSHA256) { |
| 140 TestDigest(rtc::DIGEST_SHA_256, 32); | 176 TestDigest(rtc::DIGEST_SHA_256, 32); |
| 141 } | 177 } |
| 142 | 178 |
| 143 TEST_F(SSLIdentityTest, DigestSHA384) { | 179 TEST_F(SSLIdentityTest, DigestSHA384) { |
| 144 TestDigest(rtc::DIGEST_SHA_384, 48); | 180 TestDigest(rtc::DIGEST_SHA_384, 48); |
| 145 } | 181 } |
| 146 | 182 |
| 147 TEST_F(SSLIdentityTest, DigestSHA512) { | 183 TEST_F(SSLIdentityTest, DigestSHA512) { |
| 148 TestDigest(rtc::DIGEST_SHA_512, 64); | 184 TestDigest(rtc::DIGEST_SHA_512, 64); |
| 149 } | 185 } |
| 150 | 186 |
| 151 TEST_F(SSLIdentityTest, FromPEMStrings) { | 187 TEST_F(SSLIdentityTest, FromPEMStringsRSA) { |
| 152 static const char kRSA_PRIVATE_KEY_PEM[] = | 188 static const char kRSA_PRIVATE_KEY_PEM[] = |
| 153 "-----BEGIN RSA PRIVATE KEY-----\n" | 189 "-----BEGIN RSA PRIVATE KEY-----\n" |
| 154 "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" | 190 "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" |
| 155 "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" | 191 "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n" |
| 156 "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" | 192 "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n" |
| 157 "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n" | 193 "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n" |
| 158 "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n" | 194 "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n" |
| 159 "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n" | 195 "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n" |
| 160 "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n" | 196 "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n" |
| 161 "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n" | 197 "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n" |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 179 "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" | 215 "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" |
| 180 "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" | 216 "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" |
| 181 "-----END CERTIFICATE-----\n"; | 217 "-----END CERTIFICATE-----\n"; |
| 182 | 218 |
| 183 rtc::scoped_ptr<SSLIdentity> identity( | 219 rtc::scoped_ptr<SSLIdentity> identity( |
| 184 SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM)); | 220 SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM)); |
| 185 EXPECT_TRUE(identity); | 221 EXPECT_TRUE(identity); |
| 186 EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString()); | 222 EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString()); |
| 187 } | 223 } |
| 188 | 224 |
| 225 #if SSL_USE_OPENSSL | |
| 226 // This will not work on NSS as PK11_ImportDERPrivateKeyInfoAndReturnKey is not | |
| 227 // ready for EC keys. Furthermore, NSSIdentity::FromPEMStrings is currently | |
| 228 // hardwired for RSA (the header matching via kPemTypeRsaPrivateKey needs | |
| 229 // trivial generalization). | |
| 230 TEST_F(SSLIdentityTest, FromPEMStringsEC) { | |
| 231 static const char kRSA_PRIVATE_KEY_PEM[] = | |
| 232 "-----BEGIN EC PRIVATE KEY-----\n" | |
| 233 "MHcCAQEEIKkIztWLPbs4Y2zWv7VW2Ov4is2ifleCuPgRB8fRv3IkoAoGCCqGSM49\n" | |
| 234 "AwEHoUQDQgAEDPV33NrhSdhg9cBRkUWUXnVMXc3h17i9ARbSmNgminKcBXb8/y8L\n" | |
| 235 "A76cMWQPPM0ybHO8OS7ZVg2U/m+TwE1M2g==\n" | |
| 236 "-----END EC PRIVATE KEY-----\n"; | |
| 237 static const char kCERT_PEM[] = | |
| 238 "-----BEGIN CERTIFICATE-----\n" | |
| 239 "MIIB0jCCAXmgAwIBAgIJAMCjpFt9t6LMMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT\n" | |
| 240 "AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn\n" | |
| 241 "aXRzIFB0eSBMdGQwIBcNMTUwNjMwMTMwMTIyWhgPMjI4OTA0MTMxMzAxMjJaMEUx\n" | |
| 242 "CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\n" | |
| 243 "cm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQM\n" | |
| 244 "9Xfc2uFJ2GD1wFGRRZRedUxdzeHXuL0BFtKY2CaKcpwFdvz/LwsDvpwxZA88zTJs\n" | |
| 245 "c7w5LtlWDZT+b5PATUzao1AwTjAdBgNVHQ4EFgQUYHq6nxNNIE832ZmaHc/noODO\n" | |
| 246 "rtAwHwYDVR0jBBgwFoAUYHq6nxNNIE832ZmaHc/noODOrtAwDAYDVR0TBAUwAwEB\n" | |
| 247 "/zAKBggqhkjOPQQDAgNHADBEAiAQRojsTyZG0BlKoU7gOt5h+yAMLl2cxmDtOIQr\n" | |
| 248 "GWP/PwIgJynB4AUDsPT0DWmethOXYijB5sY5UPd9DvgmiS/Mr6s=\n" | |
| 249 "-----END CERTIFICATE-----\n"; | |
| 250 | |
| 251 rtc::scoped_ptr<SSLIdentity> identity( | |
| 252 SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kCERT_PEM)); | |
| 253 EXPECT_TRUE(identity); | |
| 254 EXPECT_EQ(kCERT_PEM, identity->certificate().ToPEMString()); | |
| 255 } | |
| 256 #endif | |
| 257 | |
| 189 TEST_F(SSLIdentityTest, PemDerConversion) { | 258 TEST_F(SSLIdentityTest, PemDerConversion) { |
| 190 std::string der; | 259 std::string der; |
| 191 EXPECT_TRUE(SSLIdentity::PemToDer("CERTIFICATE", kTestCertificate, &der)); | 260 EXPECT_TRUE(SSLIdentity::PemToDer("CERTIFICATE", kTestCertificate, &der)); |
| 192 | 261 |
| 193 EXPECT_EQ(kTestCertificate, SSLIdentity::DerToPem( | 262 EXPECT_EQ(kTestCertificate, SSLIdentity::DerToPem( |
| 194 "CERTIFICATE", | 263 "CERTIFICATE", |
| 195 reinterpret_cast<const unsigned char*>(der.data()), der.length())); | 264 reinterpret_cast<const unsigned char*>(der.data()), der.length())); |
| 196 } | 265 } |
| 197 | 266 |
| 198 TEST_F(SSLIdentityTest, GetSignatureDigestAlgorithm) { | 267 TEST_F(SSLIdentityTest, GetSignatureDigestAlgorithm) { |
| 199 TestGetSignatureDigestAlgorithm(); | 268 TestGetSignatureDigestAlgorithm(); |
| 200 } | 269 } |
| OLD | NEW |