security/pkix/lib/pkixder.h

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* Copyright 2013 Mozilla Foundation
michael@0 4 *
michael@0 5 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 6 * you may not use this file except in compliance with the License.
michael@0 7 * You may obtain a copy of the License at
michael@0 8 *
michael@0 9 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 10 *
michael@0 11 * Unless required by applicable law or agreed to in writing, software
michael@0 12 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 14 * See the License for the specific language governing permissions and
michael@0 15 * limitations under the License.
michael@0 16 */
michael@0 17
michael@0 18 #ifndef mozilla_pkix__pkixder_h
michael@0 19 #define mozilla_pkix__pkixder_h
michael@0 20
michael@0 21 #include "pkix/nullptr.h"
michael@0 22
michael@0 23 #include "prerror.h"
michael@0 24 #include "prlog.h"
michael@0 25 #include "secder.h"
michael@0 26 #include "secerr.h"
michael@0 27 #include "secoidt.h"
michael@0 28 #include "stdint.h"
michael@0 29
michael@0 30 namespace mozilla { namespace pkix { namespace der {
michael@0 31
michael@0 32 enum Class
michael@0 33 {
michael@0 34 UNIVERSAL = 0 << 6,
michael@0 35 // APPLICATION = 1 << 6, // unused
michael@0 36 CONTEXT_SPECIFIC = 2 << 6,
michael@0 37 // PRIVATE = 3 << 6 // unused
michael@0 38 };
michael@0 39
michael@0 40 enum Constructed
michael@0 41 {
michael@0 42 CONSTRUCTED = 1 << 5
michael@0 43 };
michael@0 44
michael@0 45 enum Tag
michael@0 46 {
michael@0 47 BOOLEAN = UNIVERSAL | 0x01,
michael@0 48 INTEGER = UNIVERSAL | 0x02,
michael@0 49 BIT_STRING = UNIVERSAL | 0x03,
michael@0 50 OCTET_STRING = UNIVERSAL | 0x04,
michael@0 51 NULLTag = UNIVERSAL | 0x05,
michael@0 52 OIDTag = UNIVERSAL | 0x06,
michael@0 53 ENUMERATED = UNIVERSAL | 0x0a,
michael@0 54 GENERALIZED_TIME = UNIVERSAL | 0x18,
michael@0 55 SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x30,
michael@0 56 };
michael@0 57
michael@0 58 enum Result
michael@0 59 {
michael@0 60 Failure = -1,
michael@0 61 Success = 0
michael@0 62 };
michael@0 63
michael@0 64 enum EmptyAllowed { MayBeEmpty = 0, MustNotBeEmpty = 1 };
michael@0 65
michael@0 66 Result Fail(PRErrorCode errorCode);
michael@0 67
michael@0 68 class Input
michael@0 69 {
michael@0 70 public:
michael@0 71 Input()
michael@0 72 : input(nullptr)
michael@0 73 , end(nullptr)
michael@0 74 {
michael@0 75 }
michael@0 76
michael@0 77 Result Init(const uint8_t* data, size_t len)
michael@0 78 {
michael@0 79 if (input) {
michael@0 80 // already initialized
michael@0 81 return Fail(SEC_ERROR_INVALID_ARGS);
michael@0 82 }
michael@0 83 if (!data || len > 0xffffu) {
michael@0 84 // input too large
michael@0 85 return Fail(SEC_ERROR_BAD_DER);
michael@0 86 }
michael@0 87
michael@0 88 // XXX: this->input = input bug was not caught by tests! Why not?
michael@0 89 // this->end = end bug was not caught by tests! Why not?
michael@0 90 this->input = data;
michael@0 91 this->end = data + len;
michael@0 92
michael@0 93 return Success;
michael@0 94 }
michael@0 95
michael@0 96 Result Expect(const uint8_t* expected, uint16_t expectedLen)
michael@0 97 {
michael@0 98 if (EnsureLength(expectedLen) != Success) {
michael@0 99 return Fail(SEC_ERROR_BAD_DER);
michael@0 100 }
michael@0 101 if (memcmp(input, expected, expectedLen)) {
michael@0 102 return Fail(SEC_ERROR_BAD_DER);
michael@0 103 }
michael@0 104 input += expectedLen;
michael@0 105 return Success;
michael@0 106 }
michael@0 107
michael@0 108 bool Peek(uint8_t expectedByte) const
michael@0 109 {
michael@0 110 return input < end && *input == expectedByte;
michael@0 111 }
michael@0 112
michael@0 113 Result Read(uint8_t& out)
michael@0 114 {
michael@0 115 if (input == end) {
michael@0 116 return Fail(SEC_ERROR_BAD_DER);
michael@0 117 }
michael@0 118 out = *input++;
michael@0 119 return Success;
michael@0 120 }
michael@0 121
michael@0 122 Result Read(uint16_t& out)
michael@0 123 {
michael@0 124 if (input == end || input + 1 == end) {
michael@0 125 return Fail(SEC_ERROR_BAD_DER);
michael@0 126 }
michael@0 127 out = *input++;
michael@0 128 out <<= 8u;
michael@0 129 out |= *input++;
michael@0 130 return Success;
michael@0 131 }
michael@0 132
michael@0 133 Result Skip(uint16_t len)
michael@0 134 {
michael@0 135 if (EnsureLength(len) != Success) {
michael@0 136 return Fail(SEC_ERROR_BAD_DER);
michael@0 137 }
michael@0 138 input += len;
michael@0 139 return Success;
michael@0 140 }
michael@0 141
michael@0 142 Result Skip(uint16_t len, Input& skippedInput)
michael@0 143 {
michael@0 144 if (EnsureLength(len) != Success) {
michael@0 145 return Fail(SEC_ERROR_BAD_DER);
michael@0 146 }
michael@0 147 if (skippedInput.Init(input, len) != Success) {
michael@0 148 return Failure;
michael@0 149 }
michael@0 150 input += len;
michael@0 151 return Success;
michael@0 152 }
michael@0 153
michael@0 154 Result Skip(uint16_t len, SECItem& skippedItem)
michael@0 155 {
michael@0 156 if (EnsureLength(len) != Success) {
michael@0 157 return Fail(SEC_ERROR_BAD_DER);
michael@0 158 }
michael@0 159 skippedItem.type = siBuffer;
michael@0 160 skippedItem.data = const_cast<uint8_t*>(input);
michael@0 161 skippedItem.len = len;
michael@0 162 input += len;
michael@0 163 return Success;
michael@0 164 }
michael@0 165
michael@0 166 void SkipToEnd()
michael@0 167 {
michael@0 168 input = end;
michael@0 169 }
michael@0 170
michael@0 171 Result EnsureLength(uint16_t len)
michael@0 172 {
michael@0 173 if (static_cast<size_t>(end - input) < len) {
michael@0 174 return Fail(SEC_ERROR_BAD_DER);
michael@0 175 }
michael@0 176 return Success;
michael@0 177 }
michael@0 178
michael@0 179 bool AtEnd() const { return input == end; }
michael@0 180
michael@0 181 class Mark
michael@0 182 {
michael@0 183 private:
michael@0 184 friend class Input;
michael@0 185 explicit Mark(const uint8_t* mark) : mMark(mark) { }
michael@0 186 const uint8_t* const mMark;
michael@0 187 void operator=(const Mark&) /* = delete */;
michael@0 188 };
michael@0 189
michael@0 190 Mark GetMark() const { return Mark(input); }
michael@0 191
michael@0 192 bool GetSECItem(SECItemType type, const Mark& mark, /*out*/ SECItem& item)
michael@0 193 {
michael@0 194 PR_ASSERT(mark.mMark < input);
michael@0 195 item.type = type;
michael@0 196 item.data = const_cast<uint8_t*>(mark.mMark);
michael@0 197 // TODO: Return false if bounds check fails
michael@0 198 item.len = input - mark.mMark;
michael@0 199 return true;
michael@0 200 }
michael@0 201
michael@0 202 private:
michael@0 203 const uint8_t* input;
michael@0 204 const uint8_t* end;
michael@0 205
michael@0 206 Input(const Input&) /* = delete */;
michael@0 207 void operator=(const Input&) /* = delete */;
michael@0 208 };
michael@0 209
michael@0 210 inline Result
michael@0 211 ExpectTagAndLength(Input& input, uint8_t expectedTag, uint8_t expectedLength)
michael@0 212 {
michael@0 213 PR_ASSERT((expectedTag & 0x1F) != 0x1F); // high tag number form not allowed
michael@0 214 PR_ASSERT(expectedLength < 128); // must be a single-byte length
michael@0 215
michael@0 216 uint16_t tagAndLength;
michael@0 217 if (input.Read(tagAndLength) != Success) {
michael@0 218 return Failure;
michael@0 219 }
michael@0 220
michael@0 221 uint16_t expectedTagAndLength = static_cast<uint16_t>(expectedTag << 8);
michael@0 222 expectedTagAndLength |= expectedLength;
michael@0 223
michael@0 224 if (tagAndLength != expectedTagAndLength) {
michael@0 225 return Fail(SEC_ERROR_BAD_DER);
michael@0 226 }
michael@0 227
michael@0 228 return Success;
michael@0 229 }
michael@0 230
michael@0 231 Result
michael@0 232 ExpectTagAndGetLength(Input& input, uint8_t expectedTag, uint16_t& length);
michael@0 233
michael@0 234 inline Result
michael@0 235 ExpectTagAndIgnoreLength(Input& input, uint8_t expectedTag)
michael@0 236 {
michael@0 237 uint16_t ignored;
michael@0 238 return ExpectTagAndGetLength(input, expectedTag, ignored);
michael@0 239 }
michael@0 240
michael@0 241 inline Result
michael@0 242 ExpectTagAndGetValue(Input& input, uint8_t tag, /*out*/ Input& value)
michael@0 243 {
michael@0 244 uint16_t length;
michael@0 245 if (ExpectTagAndGetLength(input, tag, length) != Success) {
michael@0 246 return Failure;
michael@0 247 }
michael@0 248 return input.Skip(length, value);
michael@0 249 }
michael@0 250
michael@0 251 inline Result
michael@0 252 End(Input& input)
michael@0 253 {
michael@0 254 if (!input.AtEnd()) {
michael@0 255 return Fail(SEC_ERROR_BAD_DER);
michael@0 256 }
michael@0 257
michael@0 258 return Success;
michael@0 259 }
michael@0 260
michael@0 261 template <typename Decoder>
michael@0 262 inline Result
michael@0 263 Nested(Input& input, uint8_t tag, Decoder decoder)
michael@0 264 {
michael@0 265 uint16_t length;
michael@0 266 if (ExpectTagAndGetLength(input, tag, length) != Success) {
michael@0 267 return Failure;
michael@0 268 }
michael@0 269
michael@0 270 Input nested;
michael@0 271 if (input.Skip(length, nested) != Success) {
michael@0 272 return Failure;
michael@0 273 }
michael@0 274
michael@0 275 if (decoder(nested) != Success) {
michael@0 276 return Failure;
michael@0 277 }
michael@0 278
michael@0 279 return End(nested);
michael@0 280 }
michael@0 281
michael@0 282 template <typename Decoder>
michael@0 283 inline Result
michael@0 284 Nested(Input& input, uint8_t outerTag, uint8_t innerTag, Decoder decoder)
michael@0 285 {
michael@0 286 // XXX: This doesn't work (in VS2010):
michael@0 287 // return Nested(input, outerTag, bind(Nested, _1, innerTag, decoder));
michael@0 288
michael@0 289 uint16_t length;
michael@0 290 if (ExpectTagAndGetLength(input, outerTag, length) != Success) {
michael@0 291 return Failure;
michael@0 292 }
michael@0 293 Input nestedInput;
michael@0 294 if (input.Skip(length, nestedInput) != Success) {
michael@0 295 return Failure;
michael@0 296 }
michael@0 297 if (Nested(nestedInput, innerTag, decoder) != Success) {
michael@0 298 return Failure;
michael@0 299 }
michael@0 300
michael@0 301 return End(nestedInput);
michael@0 302 }
michael@0 303
michael@0 304 // This can be used to decode constructs like this:
michael@0 305 //
michael@0 306 // ...
michael@0 307 // foos SEQUENCE OF Foo,
michael@0 308 // ...
michael@0 309 // Foo ::= SEQUENCE {
michael@0 310 // }
michael@0 311 //
michael@0 312 // using a call like this:
michael@0 313 //
michael@0 314 // rv = NestedOf(input, SEQEUENCE, SEQUENCE, bind(_1, Foo));
michael@0 315 //
michael@0 316 // Result Foo(Input& input) {
michael@0 317 // }
michael@0 318 //
michael@0 319 // In this example, Foo will get called once for each element of foos.
michael@0 320 //
michael@0 321 template <typename Decoder>
michael@0 322 inline Result
michael@0 323 NestedOf(Input& input, uint8_t outerTag, uint8_t innerTag,
michael@0 324 EmptyAllowed mayBeEmpty, Decoder decoder)
michael@0 325 {
michael@0 326 uint16_t responsesLength;
michael@0 327 if (ExpectTagAndGetLength(input, outerTag, responsesLength) != Success) {
michael@0 328 return Failure;
michael@0 329 }
michael@0 330
michael@0 331 Input inner;
michael@0 332 if (input.Skip(responsesLength, inner) != Success) {
michael@0 333 return Failure;
michael@0 334 }
michael@0 335
michael@0 336 if (inner.AtEnd()) {
michael@0 337 if (mayBeEmpty != MayBeEmpty) {
michael@0 338 return Fail(SEC_ERROR_BAD_DER);
michael@0 339 }
michael@0 340 return Success;
michael@0 341 }
michael@0 342
michael@0 343 do {
michael@0 344 if (Nested(inner, innerTag, decoder) != Success) {
michael@0 345 return Failure;
michael@0 346 }
michael@0 347 } while (!inner.AtEnd());
michael@0 348
michael@0 349 return Success;
michael@0 350 }
michael@0 351
michael@0 352 inline Result
michael@0 353 Skip(Input& input, uint8_t tag)
michael@0 354 {
michael@0 355 uint16_t length;
michael@0 356 if (ExpectTagAndGetLength(input, tag, length) != Success) {
michael@0 357 return Failure;
michael@0 358 }
michael@0 359 return input.Skip(length);
michael@0 360 }
michael@0 361
michael@0 362 inline Result
michael@0 363 Skip(Input& input, uint8_t tag, /*out*/ SECItem& value)
michael@0 364 {
michael@0 365 uint16_t length;
michael@0 366 if (ExpectTagAndGetLength(input, tag, length) != Success) {
michael@0 367 return Failure;
michael@0 368 }
michael@0 369 return input.Skip(length, value);
michael@0 370 }
michael@0 371
michael@0 372 // Universal types
michael@0 373
michael@0 374 inline Result
michael@0 375 Boolean(Input& input, /*out*/ bool& value)
michael@0 376 {
michael@0 377 if (ExpectTagAndLength(input, BOOLEAN, 1) != Success) {
michael@0 378 return Failure;
michael@0 379 }
michael@0 380
michael@0 381 uint8_t intValue;
michael@0 382 if (input.Read(intValue) != Success) {
michael@0 383 return Failure;
michael@0 384 }
michael@0 385 switch (intValue) {
michael@0 386 case 0: value = false; return Success;
michael@0 387 case 0xFF: value = true; return Success;
michael@0 388 default:
michael@0 389 PR_SetError(SEC_ERROR_BAD_DER, 0);
michael@0 390 return Failure;
michael@0 391 }
michael@0 392 }
michael@0 393
michael@0 394 // This is for any BOOLEAN DEFAULT FALSE.
michael@0 395 // (If it is present and false, this is a bad encoding.)
michael@0 396 // TODO(bug 989518): For compatibility reasons, in some places we allow
michael@0 397 // invalid encodings with the explicit default value.
michael@0 398 inline Result
michael@0 399 OptionalBoolean(Input& input, bool allowInvalidExplicitEncoding,
michael@0 400 /*out*/ bool& value)
michael@0 401 {
michael@0 402 value = false;
michael@0 403 if (input.Peek(BOOLEAN)) {
michael@0 404 if (Boolean(input, value) != Success) {
michael@0 405 return Failure;
michael@0 406 }
michael@0 407 if (!allowInvalidExplicitEncoding && !value) {
michael@0 408 return Fail(SEC_ERROR_BAD_DER);
michael@0 409 }
michael@0 410 }
michael@0 411 return Success;
michael@0 412 }
michael@0 413
michael@0 414 inline Result
michael@0 415 Enumerated(Input& input, uint8_t& value)
michael@0 416 {
michael@0 417 if (ExpectTagAndLength(input, ENUMERATED | 0, 1) != Success) {
michael@0 418 return Failure;
michael@0 419 }
michael@0 420 return input.Read(value);
michael@0 421 }
michael@0 422
michael@0 423 inline Result
michael@0 424 GeneralizedTime(Input& input, PRTime& time)
michael@0 425 {
michael@0 426 uint16_t length;
michael@0 427 SECItem encoded;
michael@0 428 if (ExpectTagAndGetLength(input, GENERALIZED_TIME, length) != Success) {
michael@0 429 return Failure;
michael@0 430 }
michael@0 431 if (input.Skip(length, encoded)) {
michael@0 432 return Failure;
michael@0 433 }
michael@0 434 if (DER_GeneralizedTimeToTime(&time, &encoded) != SECSuccess) {
michael@0 435 return Failure;
michael@0 436 }
michael@0 437
michael@0 438 return Success;
michael@0 439 }
michael@0 440
michael@0 441 inline Result
michael@0 442 Integer(Input& input, /*out*/ SECItem& value)
michael@0 443 {
michael@0 444 uint16_t length;
michael@0 445 if (ExpectTagAndGetLength(input, INTEGER, length) != Success) {
michael@0 446 return Failure;
michael@0 447 }
michael@0 448
michael@0 449 if (input.Skip(length, value) != Success) {
michael@0 450 return Failure;
michael@0 451 }
michael@0 452
michael@0 453 if (value.len == 0) {
michael@0 454 return Fail(SEC_ERROR_BAD_DER);
michael@0 455 }
michael@0 456
michael@0 457 // Check for overly-long encodings. If the first byte is 0x00 then the high
michael@0 458 // bit on the second byte must be 1; otherwise the same *positive* value
michael@0 459 // could be encoded without the leading 0x00 byte. If the first byte is 0xFF
michael@0 460 // then the second byte must NOT have its high bit set; otherwise the same
michael@0 461 // *negative* value could be encoded without the leading 0xFF byte.
michael@0 462 if (value.len > 1) {
michael@0 463 if ((value.data[0] == 0x00 && (value.data[1] & 0x80) == 0) ||
michael@0 464 (value.data[0] == 0xff && (value.data[1] & 0x80) != 0)) {
michael@0 465 return Fail(SEC_ERROR_BAD_DER);
michael@0 466 }
michael@0 467 }
michael@0 468
michael@0 469 return Success;
michael@0 470 }
michael@0 471
michael@0 472 inline Result
michael@0 473 Null(Input& input)
michael@0 474 {
michael@0 475 return ExpectTagAndLength(input, NULLTag, 0);
michael@0 476 }
michael@0 477
michael@0 478 template <uint16_t Len>
michael@0 479 Result
michael@0 480 OID(Input& input, const uint8_t (&expectedOid)[Len])
michael@0 481 {
michael@0 482 if (ExpectTagAndLength(input, OIDTag, Len) != Success) {
michael@0 483 return Failure;
michael@0 484 }
michael@0 485
michael@0 486 return input.Expect(expectedOid, Len);
michael@0 487 }
michael@0 488
michael@0 489 // PKI-specific types
michael@0 490
michael@0 491 // AlgorithmIdentifier ::= SEQUENCE {
michael@0 492 // algorithm OBJECT IDENTIFIER,
michael@0 493 // parameters ANY DEFINED BY algorithm OPTIONAL }
michael@0 494 inline Result
michael@0 495 AlgorithmIdentifier(Input& input, SECAlgorithmID& algorithmID)
michael@0 496 {
michael@0 497 if (Skip(input, OIDTag, algorithmID.algorithm) != Success) {
michael@0 498 return Failure;
michael@0 499 }
michael@0 500 algorithmID.parameters.data = nullptr;
michael@0 501 algorithmID.parameters.len = 0;
michael@0 502 if (input.AtEnd()) {
michael@0 503 return Success;
michael@0 504 }
michael@0 505 return Null(input);
michael@0 506 }
michael@0 507
michael@0 508 inline Result
michael@0 509 CertificateSerialNumber(Input& input, /*out*/ SECItem& serialNumber)
michael@0 510 {
michael@0 511 // http://tools.ietf.org/html/rfc5280#section-4.1.2.2:
michael@0 512 //
michael@0 513 // * "The serial number MUST be a positive integer assigned by the CA to
michael@0 514 // each certificate."
michael@0 515 // * "Certificate users MUST be able to handle serialNumber values up to 20
michael@0 516 // octets. Conforming CAs MUST NOT use serialNumber values longer than 20
michael@0 517 // octets."
michael@0 518 // * "Note: Non-conforming CAs may issue certificates with serial numbers
michael@0 519 // that are negative or zero. Certificate users SHOULD be prepared to
michael@0 520 // gracefully handle such certificates."
michael@0 521
michael@0 522 return Integer(input, serialNumber);
michael@0 523 }
michael@0 524
michael@0 525 // x.509 and OCSP both use this same version numbering scheme, though OCSP
michael@0 526 // only supports v1.
michael@0 527 enum Version { v1 = 0, v2 = 1, v3 = 2 };
michael@0 528
michael@0 529 // X.509 Certificate and OCSP ResponseData both use this
michael@0 530 // "[0] EXPLICIT Version DEFAULT <defaultVersion>" construct, but with
michael@0 531 // different default versions.
michael@0 532 inline Result
michael@0 533 OptionalVersion(Input& input, /*out*/ uint8_t& version)
michael@0 534 {
michael@0 535 const uint8_t tag = CONTEXT_SPECIFIC | CONSTRUCTED | 0;
michael@0 536 if (!input.Peek(tag)) {
michael@0 537 version = v1;
michael@0 538 return Success;
michael@0 539 }
michael@0 540 if (ExpectTagAndLength(input, tag, 3) != Success) {
michael@0 541 return Failure;
michael@0 542 }
michael@0 543 if (ExpectTagAndLength(input, INTEGER, 1) != Success) {
michael@0 544 return Failure;
michael@0 545 }
michael@0 546 if (input.Read(version) != Success) {
michael@0 547 return Failure;
michael@0 548 }
michael@0 549 if (version & 0x80) { // negative
michael@0 550 return Fail(SEC_ERROR_BAD_DER);
michael@0 551 }
michael@0 552 return Success;
michael@0 553 }
michael@0 554
michael@0 555 } } } // namespace mozilla::pkix::der
michael@0 556
michael@0 557 #endif // mozilla_pkix__pkixder_h

mercurial