// 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 }
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) }