// Package der checks that bytes are one element in the Distinguished Encoding // Rules of X.690, as spec v0.11 ยง29.10 asks of a CMS signature before it // looks inside it: definite and minimal lengths, no high tag numbers, no // constructed form of a type that DER only has primitive, canonical BOOLEAN, // INTEGER, NULL, OBJECT IDENTIFIER, BIT STRING, UTCTime and GeneralizedTime, // and no bytes after the element. Go's encoding/asn1 accepts some of what DER // forbids, and a signature that two implementations read the same must not // depend on it. // // The order of the elements of a SET OF cannot be checked without a schema: // SetOfSorted does it for the callers that know theirs. package der import ( "bytes" "errors" "fmt" "strings" "time" ) const maxDepth = 32 // Check returns nil when b is exactly one DER element. func Check(b []byte) error { n, err := check(b, 0) if err != nil { return err } if n != len(b) { return fmt.Errorf("der: %d bytes after the element", len(b)-n) } return nil } // Split returns the content of the DER element b, which must be constructed, // as the encodings of its children, and the identifier octet of b. It // assumes Check passed. func Split(b []byte) (id byte, children [][]byte, err error) { if len(b) == 0 || b[0]&0x20 == 0 { return 0, nil, errors.New("der: not a constructed element") } hl, cl, err := header(b) if err != nil { return 0, nil, err } rest := b[hl : hl+cl] for len(rest) > 0 { h, c, err := header(rest) if err != nil { return 0, nil, err } children = append(children, rest[:h+c]) rest = rest[h+c:] } return b[0], children, nil } // Content returns the content octets of the DER element b. func Content(b []byte) ([]byte, error) { hl, cl, err := header(b) if err != nil { return nil, err } return b[hl : hl+cl], nil } // SetOfSorted reports whether the encodings are in ascending order of their // bytes, as DER requires of the elements of a SET OF (X.690 11.6). Equal // elements may repeat, side by side: X.690 does not forbid it, and a time- // stamping authority may send its certificate twice. Whoever counts the // elements of a SET OF decides what a repetition means. func SetOfSorted(elems [][]byte) bool { for i := 1; i < len(elems); i++ { if bytes.Compare(elems[i-1], elems[i]) > 0 { return false } } return true } // ParseTime reads a UTCTime or a GeneralizedTime element as DER writes them // (X.690 11.7 and 11.8): YYMMDDHHMMSSZ, with the years 50 to 99 in the 20th // century (RFC 5280 4.1.2.5.1), or YYYYMMDDHHMMSS, an optional fraction of // seconds without a trailing zero, and Z. A date or a time that does not // exist, a second 60 included, is refused. fraction reports whether a // GeneralizedTime has one. func ParseTime(b []byte) (t time.Time, fraction bool, err error) { if len(b) < 2 || (b[0] != 0x17 && b[0] != 0x18) { return time.Time{}, false, errors.New("der: not a UTCTime or a GeneralizedTime") } c, err := Content(b) if err != nil { return time.Time{}, false, err } return parseTime(b[0], string(c)) } func parseTime(tag byte, s string) (time.Time, bool, error) { bad := errors.New("der: a time that is not in the form of DER") if len(s) < 13 || s[len(s)-1] != 'Z' { return time.Time{}, false, bad } body, frac := s[:len(s)-1], "" var year int switch tag { case 0x17: if len(body) != 12 || !digits(body) { return time.Time{}, false, bad } year = atoi(body[:2]) + 1900 if year < 1950 { year += 100 } body = body[2:] default: if dot := strings.IndexByte(body, '.'); dot >= 0 { body, frac = body[:dot], body[dot+1:] if frac == "" || frac[len(frac)-1] == '0' || !digits(frac) { return time.Time{}, false, bad } } if len(body) != 14 || !digits(body) { return time.Time{}, false, bad } year = atoi(body[:4]) body = body[4:] } month, day, hour, minute, second := atoi(body[0:2]), atoi(body[2:4]), atoi(body[4:6]), atoi(body[6:8]), atoi(body[8:10]) if month < 1 || month > 12 || day < 1 || hour > 23 || minute > 59 || second > 59 || day > time.Date(year, time.Month(month)+1, 0, 0, 0, 0, 0, time.UTC).Day() { return time.Time{}, false, errors.New("der: a date or a time that does not exist") } nanos := 0 for i := 0; i < 9; i++ { nanos *= 10 if i < len(frac) { nanos += int(frac[i] - '0') } } return time.Date(year, time.Month(month), day, hour, minute, second, nanos, time.UTC), frac != "", nil } func digits(s string) bool { for i := 0; i < len(s); i++ { if s[i] < '0' || s[i] > '9' { return false } } return s != "" } func atoi(s string) int { n := 0 for i := 0; i < len(s); i++ { n = n*10 + int(s[i]-'0') } return n } // header returns the length of the identifier and length octets of the // element at the start of b, and the length of its content, which must fit // in b. func header(b []byte) (headerLen, contentLen int, err error) { if len(b) < 2 { return 0, 0, errors.New("der: truncated element") } if b[0]&0x1f == 0x1f { return 0, 0, errors.New("der: a tag number of 31 or more") } l := b[1] switch { case l < 0x80: headerLen, contentLen = 2, int(l) case l == 0x80: return 0, 0, errors.New("der: an indefinite length") case l == 0xff: return 0, 0, errors.New("der: a length of 0xff") default: n := int(l & 0x7f) if n > 4 || len(b) < 2+n { return 0, 0, errors.New("der: a length that does not fit") } if b[2] == 0 { return 0, 0, errors.New("der: a length with a leading zero") } v := 0 for _, c := range b[2 : 2+n] { v = v<<8 | int(c) } if v < 0x80 { return 0, 0, errors.New("der: a long form for a length under 128") } headerLen, contentLen = 2+n, v } if contentLen < 0 || contentLen > len(b)-headerLen { return 0, 0, errors.New("der: an element longer than its container") } return headerLen, contentLen, nil } // check validates the element at the start of b and returns its length. func check(b []byte, depth int) (int, error) { if depth > maxDepth { return 0, errors.New("der: nested too deep") } hl, cl, err := header(b) if err != nil { return 0, err } content := b[hl : hl+cl] id := b[0] class, constructed, tag := id>>6, id&0x20 != 0, id&0x1f if constructed { if class == 0 && tag != 16 && tag != 17 { return 0, fmt.Errorf("der: constructed form of the universal type %d", tag) } for len(content) > 0 { n, err := check(content, depth+1) if err != nil { return 0, err } content = content[n:] } return hl + cl, nil } if class == 0 { if err := checkPrimitive(tag, content); err != nil { return 0, err } } return hl + cl, nil } func checkPrimitive(tag byte, c []byte) error { switch tag { case 1: // BOOLEAN if len(c) != 1 || (c[0] != 0 && c[0] != 0xff) { return errors.New("der: a BOOLEAN that is not 00 or FF") } case 2, 10: // INTEGER, ENUMERATED if len(c) == 0 { return errors.New("der: an empty INTEGER") } if len(c) > 1 && (c[0] == 0 && c[1]&0x80 == 0 || c[0] == 0xff && c[1]&0x80 != 0) { return errors.New("der: an INTEGER that is not minimal") } case 3: // BIT STRING if len(c) == 0 || c[0] > 7 || len(c) == 1 && c[0] != 0 { return errors.New("der: a malformed BIT STRING") } if len(c) > 1 && c[0] != 0 && c[len(c)-1]&(1<