Beispiel #1
0
// decodeTagString decodes s as a tag descriptor and returns the decoded tag or
// an error.
func decodeTagString(s string) (lib.Tag, error) {
	ss := strings.Split(s, " ")

	// Tag aliases may only be in the first component.
	tag, ok := lib.TagByName(ss[0])
	if ok {
		ss = ss[1:]
	} else {
		// Tags default to constructed, context-specific.
		tag.Class = lib.ClassContextSpecific
		tag.Constructed = true

		// Otherwise, the first component is an optional class.
		switch ss[0] {
		case "APPLICATION":
			tag.Class = lib.ClassApplication
			ss = ss[1:]
		case "PRIVATE":
			tag.Class = lib.ClassPrivate
			ss = ss[1:]
		case "UNIVERSAL":
			tag.Class = lib.ClassUniversal
			ss = ss[1:]
		}

		// The next (or first) component must be the tag number.
		if len(ss) == 0 {
			return lib.Tag{}, errors.New("expected tag number")
		}
		n, err := strconv.ParseUint(ss[0], 10, 32)
		if err != nil {
			return lib.Tag{}, err
		}
		tag.Number = uint32(n)
		ss = ss[1:]
	}

	// The final token, if any, may be CONSTRUCTED or PRIMITIVE.
	if len(ss) > 0 {
		switch ss[0] {
		case "CONSTRUCTED":
			tag.Constructed = true
		case "PRIMITIVE":
			tag.Constructed = false
		default:
			return lib.Tag{}, fmt.Errorf("unexpected tag component '%s'", ss[0])
		}
		ss = ss[1:]
	}

	if len(ss) != 0 {
		return lib.Tag{}, fmt.Errorf("excess tag component '%s'", ss[0])
	}

	return tag, nil
}
Beispiel #2
0
func (s *scanner) Next() (token, error) {
again:
	if s.isEOF() {
		return token{Kind: tokenEOF, Pos: s.pos}, nil
	}

	switch s.text[s.pos.Offset] {
	case ' ', '\t', '\n', '\r':
		// Skip whitespace.
		s.advance()
		goto again
	case '#':
		// Skip to the end of the comment.
		s.advance()
		for !s.isEOF() {
			wasNewline := s.text[s.pos.Offset] == '\n'
			s.advance()
			if wasNewline {
				break
			}
		}
		goto again
	case '{':
		s.advance()
		return token{Kind: tokenLeftCurly, Pos: s.pos}, nil
	case '}':
		s.advance()
		return token{Kind: tokenRightCurly, Pos: s.pos}, nil
	case '"':
		return s.parseQuotedString()
	case 'u':
		if s.pos.Offset+1 < len(s.text) && s.text[s.pos.Offset+1] == '"' {
			return s.parseUTF16String()
		}
	case '`':
		s.advance()
		hexStr, ok := s.consumeUpTo('`')
		if !ok {
			return token{}, &parseError{s.pos, errors.New("unmatched `")}
		}
		bytes, err := hex.DecodeString(hexStr)
		if err != nil {
			return token{}, &parseError{s.pos, err}
		}
		return token{Kind: tokenBytes, Value: bytes, Pos: s.pos}, nil
	case '[':
		s.advance()
		tagStr, ok := s.consumeUpTo(']')
		if !ok {
			return token{}, &parseError{s.pos, errors.New("unmatched [")}
		}
		tag, err := decodeTagString(tagStr)
		if err != nil {
			return token{}, &parseError{s.pos, err}
		}
		return token{Kind: tokenBytes, Value: appendTag(nil, tag), Pos: s.pos}, nil
	}

	// Normal token. Consume up to the next whitespace character, symbol, or
	// EOF.
	start := s.pos
	s.advance()
loop:
	for !s.isEOF() {
		switch s.text[s.pos.Offset] {
		case ' ', '\t', '\n', '\r', '{', '}', '[', ']', '`', '"', '#':
			break loop
		default:
			s.advance()
		}
	}

	symbol := s.text[start.Offset:s.pos.Offset]

	// See if it is a tag.
	tag, ok := lib.TagByName(symbol)
	if ok {
		return token{Kind: tokenBytes, Value: appendTag(nil, tag), Pos: start}, nil
	}

	if regexpInteger.MatchString(symbol) {
		value, err := strconv.ParseInt(symbol, 10, 64)
		if err != nil {
			return token{}, &parseError{start, err}
		}
		return token{Kind: tokenBytes, Value: appendInteger(nil, value), Pos: s.pos}, nil
	}

	if regexpOID.MatchString(symbol) {
		oidStr := strings.Split(symbol, ".")
		var oid []uint32
		for _, s := range oidStr {
			u, err := strconv.ParseUint(s, 10, 32)
			if err != nil {
				return token{}, &parseError{start, err}
			}
			oid = append(oid, uint32(u))
		}
		der, ok := appendObjectIdentifier(nil, oid)
		if !ok {
			return token{}, errors.New("invalid OID")
		}
		return token{Kind: tokenBytes, Value: der, Pos: s.pos}, nil
	}

	if symbol == "TRUE" {
		return token{Kind: tokenBytes, Value: []byte{0xff}, Pos: s.pos}, nil
	}

	if symbol == "FALSE" {
		return token{Kind: tokenBytes, Value: []byte{0x00}, Pos: s.pos}, nil
	}

	return token{}, fmt.Errorf("unrecognized symbol '%s'", symbol)
}