All files / src ASN1Validator.ts

86.76% Statements 59/68
76.31% Branches 29/38
100% Functions 8/8
86.36% Lines 57/66

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137  1x   1x 1x 1x     1x   1x 3x     3x 3x 3x       21x 6x   20x 5x         30x         30x 3x       27x 5x     5x   27x 13x         10x 10x 31x 31x       31x 3x 3x         2x       29x       29x         37x       37x 4x 33x 24x 9x 5x 5x 5x   4x 2x 2x             1x     1x   1x     1x             34x       13x 13x 13x         4x 5x 5x   1x   4x        
import {ASN1ComponentType} from './asn1/ASN1ComponentType';
import {ASN1DefinedType} from './asn1/ASN1DefinedType';
import {ASN1Module} from './asn1/ASN1Module';
import {ASN1NamedType} from './asn1/ASN1NamedType';
import {ASN1Sequence} from './asn1/ASN1Sequence';
import {ASN1TaggedType} from './asn1/ASN1TaggedType';
import {ASN1Message} from './interfaces/ASN1Message';
import {ASN1Assignment} from './asn1/ASN1Assignment';
import {ASN1ChoiceType} from './asn1/ASN1ChoiceType';
 
export class ASN1Validator {
  constructor(private module: ASN1Module) {}
 
  validate(input: ASN1Message, type: string) {
    const assignment = this.module.getAssignment(type);
    this.validateAssignment(input, assignment);
    input.tagDefinedType = assignment.name;
  }
 
  validateAssignment(input: ASN1Message, assignment: ASN1Assignment) {
    if (assignment.type instanceof ASN1TaggedType) {
      this.validateTaggedType(assignment.type, input);
    }
    if (assignment.type instanceof ASN1Sequence) {
      this.validateSequence(assignment.type, input);
    }
  }
 
  validateTaggedType(taggedType: ASN1TaggedType, input: ASN1Message) {
    Iif (input.tagClass !== taggedType.tag.tagClass) {
      throw new Error(
        `taggedType tagClass differ: ${input.tagClass} vs ${taggedType.tag.tagClass}`
      );
    }
    if (input.tagCode !== taggedType.tag.tagCode) {
      throw new Error(
        `taggedType tagCode differ: ${input.tagCode} vs ${taggedType.tag.tagCode}`
      );
    }
    if (taggedType.type instanceof ASN1Sequence) {
      const subInput = taggedType.implicit
        ? input
        : (input.value as ASN1Message[])[0];
      this.validateSequence(taggedType.type, subInput);
    }
    if (taggedType.type instanceof ASN1DefinedType) {
      this.validateDefinedType(taggedType.type, input);
    }
  }
 
  validateSequence(sequence: ASN1Sequence, input: ASN1Message) {
    let c = 0;
    for (let i = 0; i < sequence.components.length; i++) {
      const component = sequence.components[i];
      Iif (!(component instanceof ASN1NamedType)) {
        throw new Error('limitation: cannot process only NamedType.');
      }
      // manage optional case
      if (component.optional) {
        try {
          this.validateComponent(
            sequence.components[i],
            (input.value as ASN1Message[])[c]
          );
        } catch (e) {
          continue;
        }
      }
 
      this.validateComponent(
        sequence.components[i],
        (input.value as ASN1Message[])[c]
      );
      c++;
    }
  }
 
  validateComponent(component: ASN1ComponentType, input: ASN1Message) {
    Iif (!(component instanceof ASN1NamedType)) {
      throw new Error('can process only ASN1NamedType');
    }
 
    if (component.type instanceof ASN1ChoiceType) {
      this.validateChoiceType(component.type, input);
    } else if (component.type instanceof ASN1TaggedType) {
      this.validateTaggedType(component.type, input);
    } else if (component.type instanceof ASN1DefinedType) {
      input.tagDefinedType = component.type.name;
      const assignment = this.module.getAssignment(component.type.name);
      this.validateAssignment(input, assignment);
    } else {
      if (input.value !== null) {
        const name = component.type.constructor.name;
        switch (name) {
          case 'ASN1BooleanType':
            if (typeof input.value !== 'boolean') {
              throw new Error('must be an boolean');
            }
            break;
          case 'ASN1IntegerType':
            Iif (!Number.isInteger(input.value)) {
              throw new Error('must be an integer');
            }
            break;
          case 'ASN1IA5StringType':
            Iif (typeof input.value !== 'string') {
              throw new Error(`must be an string: ${input}`);
            }
            break;
          default:
            break;
        }
      }
    }
 
    input.tagName = component.name;
  }
 
  validateDefinedType(type: ASN1DefinedType, input: ASN1Message) {
    input.tagDefinedType = type.name;
    const assignment = this.module.getAssignment(type.name);
    this.validateAssignment((input.value as ASN1Message[])[0], assignment);
  }
 
  validateChoiceType(type: ASN1ChoiceType, input: ASN1Message) {
    // try to validate the first choice, if not ok, then the second, if not ok, then the third...
    for (let i = 0; i < type.alternatives.length; i++) {
      try {
        this.validateComponent(type.alternatives[i], input);
      } catch (e) {
        continue;
      }
      break;
    }
  }
}