OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / text / secure / precis / profile.go
1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package precis
6
7 import (
8         "bytes"
9         "errors"
10         "unicode/utf8"
11
12         "golang.org/x/text/cases"
13         "golang.org/x/text/language"
14         "golang.org/x/text/runes"
15         "golang.org/x/text/secure/bidirule"
16         "golang.org/x/text/transform"
17         "golang.org/x/text/width"
18 )
19
20 var (
21         errDisallowedRune = errors.New("precis: disallowed rune encountered")
22 )
23
24 var dpTrie = newDerivedPropertiesTrie(0)
25
26 // A Profile represents a set of rules for normalizing and validating strings in
27 // the PRECIS framework.
28 type Profile struct {
29         options
30         class *class
31 }
32
33 // NewIdentifier creates a new PRECIS profile based on the Identifier string
34 // class. Profiles created from this class are suitable for use where safety is
35 // prioritized over expressiveness like network identifiers, user accounts, chat
36 // rooms, and file names.
37 func NewIdentifier(opts ...Option) *Profile {
38         return &Profile{
39                 options: getOpts(opts...),
40                 class:   identifier,
41         }
42 }
43
44 // NewFreeform creates a new PRECIS profile based on the Freeform string class.
45 // Profiles created from this class are suitable for use where expressiveness is
46 // prioritized over safety like passwords, and display-elements such as
47 // nicknames in a chat room.
48 func NewFreeform(opts ...Option) *Profile {
49         return &Profile{
50                 options: getOpts(opts...),
51                 class:   freeform,
52         }
53 }
54
55 // NewTransformer creates a new transform.Transformer that performs the PRECIS
56 // preparation and enforcement steps on the given UTF-8 encoded bytes.
57 func (p *Profile) NewTransformer() *Transformer {
58         var ts []transform.Transformer
59
60         // These transforms are applied in the order defined in
61         // https://tools.ietf.org/html/rfc7564#section-7
62
63         if p.options.foldWidth {
64                 ts = append(ts, width.Fold)
65         }
66
67         for _, f := range p.options.additional {
68                 ts = append(ts, f())
69         }
70
71         if p.options.cases != nil {
72                 ts = append(ts, p.options.cases)
73         }
74
75         ts = append(ts, p.options.norm)
76
77         if p.options.bidiRule {
78                 ts = append(ts, bidirule.New())
79         }
80
81         ts = append(ts, &checker{p: p, allowed: p.Allowed()})
82
83         // TODO: Add the disallow empty rule with a dummy transformer?
84
85         return &Transformer{transform.Chain(ts...)}
86 }
87
88 var errEmptyString = errors.New("precis: transformation resulted in empty string")
89
90 type buffers struct {
91         src  []byte
92         buf  [2][]byte
93         next int
94 }
95
96 func (b *buffers) apply(t transform.SpanningTransformer) (err error) {
97         n, err := t.Span(b.src, true)
98         if err != transform.ErrEndOfSpan {
99                 return err
100         }
101         x := b.next & 1
102         if b.buf[x] == nil {
103                 b.buf[x] = make([]byte, 0, 8+len(b.src)+len(b.src)>>2)
104         }
105         span := append(b.buf[x][:0], b.src[:n]...)
106         b.src, _, err = transform.Append(t, span, b.src[n:])
107         b.buf[x] = b.src
108         b.next++
109         return err
110 }
111
112 // Pre-allocate transformers when possible. In some cases this avoids allocation.
113 var (
114         foldWidthT transform.SpanningTransformer = width.Fold
115         lowerCaseT transform.SpanningTransformer = cases.Lower(language.Und, cases.HandleFinalSigma(false))
116 )
117
118 // TODO: make this a method on profile.
119
120 func (b *buffers) enforce(p *Profile, src []byte, comparing bool) (str []byte, err error) {
121         b.src = src
122
123         ascii := true
124         for _, c := range src {
125                 if c >= utf8.RuneSelf {
126                         ascii = false
127                         break
128                 }
129         }
130         // ASCII fast path.
131         if ascii {
132                 for _, f := range p.options.additional {
133                         if err = b.apply(f()); err != nil {
134                                 return nil, err
135                         }
136                 }
137                 switch {
138                 case p.options.asciiLower || (comparing && p.options.ignorecase):
139                         for i, c := range b.src {
140                                 if 'A' <= c && c <= 'Z' {
141                                         b.src[i] = c ^ 1<<5
142                                 }
143                         }
144                 case p.options.cases != nil:
145                         b.apply(p.options.cases)
146                 }
147                 c := checker{p: p}
148                 if _, err := c.span(b.src, true); err != nil {
149                         return nil, err
150                 }
151                 if p.disallow != nil {
152                         for _, c := range b.src {
153                                 if p.disallow.Contains(rune(c)) {
154                                         return nil, errDisallowedRune
155                                 }
156                         }
157                 }
158                 if p.options.disallowEmpty && len(b.src) == 0 {
159                         return nil, errEmptyString
160                 }
161                 return b.src, nil
162         }
163
164         // These transforms are applied in the order defined in
165         // https://tools.ietf.org/html/rfc7564#section-7
166
167         // TODO: allow different width transforms options.
168         if p.options.foldWidth || (p.options.ignorecase && comparing) {
169                 b.apply(foldWidthT)
170         }
171         for _, f := range p.options.additional {
172                 if err = b.apply(f()); err != nil {
173                         return nil, err
174                 }
175         }
176         if p.options.cases != nil {
177                 b.apply(p.options.cases)
178         }
179         if comparing && p.options.ignorecase {
180                 b.apply(lowerCaseT)
181         }
182         b.apply(p.norm)
183         if p.options.bidiRule && !bidirule.Valid(b.src) {
184                 return nil, bidirule.ErrInvalid
185         }
186         c := checker{p: p}
187         if _, err := c.span(b.src, true); err != nil {
188                 return nil, err
189         }
190         if p.disallow != nil {
191                 for i := 0; i < len(b.src); {
192                         r, size := utf8.DecodeRune(b.src[i:])
193                         if p.disallow.Contains(r) {
194                                 return nil, errDisallowedRune
195                         }
196                         i += size
197                 }
198         }
199         if p.options.disallowEmpty && len(b.src) == 0 {
200                 return nil, errEmptyString
201         }
202         return b.src, nil
203 }
204
205 // Append appends the result of applying p to src writing the result to dst.
206 // It returns an error if the input string is invalid.
207 func (p *Profile) Append(dst, src []byte) ([]byte, error) {
208         var buf buffers
209         b, err := buf.enforce(p, src, false)
210         if err != nil {
211                 return nil, err
212         }
213         return append(dst, b...), nil
214 }
215
216 func processBytes(p *Profile, b []byte, key bool) ([]byte, error) {
217         var buf buffers
218         b, err := buf.enforce(p, b, key)
219         if err != nil {
220                 return nil, err
221         }
222         if buf.next == 0 {
223                 c := make([]byte, len(b))
224                 copy(c, b)
225                 return c, nil
226         }
227         return b, nil
228 }
229
230 // Bytes returns a new byte slice with the result of applying the profile to b.
231 func (p *Profile) Bytes(b []byte) ([]byte, error) {
232         return processBytes(p, b, false)
233 }
234
235 // AppendCompareKey appends the result of applying p to src (including any
236 // optional rules to make strings comparable or useful in a map key such as
237 // applying lowercasing) writing the result to dst. It returns an error if the
238 // input string is invalid.
239 func (p *Profile) AppendCompareKey(dst, src []byte) ([]byte, error) {
240         var buf buffers
241         b, err := buf.enforce(p, src, true)
242         if err != nil {
243                 return nil, err
244         }
245         return append(dst, b...), nil
246 }
247
248 func processString(p *Profile, s string, key bool) (string, error) {
249         var buf buffers
250         b, err := buf.enforce(p, []byte(s), key)
251         if err != nil {
252                 return "", err
253         }
254         return string(b), nil
255 }
256
257 // String returns a string with the result of applying the profile to s.
258 func (p *Profile) String(s string) (string, error) {
259         return processString(p, s, false)
260 }
261
262 // CompareKey returns a string that can be used for comparison, hashing, or
263 // collation.
264 func (p *Profile) CompareKey(s string) (string, error) {
265         return processString(p, s, true)
266 }
267
268 // Compare enforces both strings, and then compares them for bit-string identity
269 // (byte-for-byte equality). If either string cannot be enforced, the comparison
270 // is false.
271 func (p *Profile) Compare(a, b string) bool {
272         var buf buffers
273
274         akey, err := buf.enforce(p, []byte(a), true)
275         if err != nil {
276                 return false
277         }
278
279         buf = buffers{}
280         bkey, err := buf.enforce(p, []byte(b), true)
281         if err != nil {
282                 return false
283         }
284
285         return bytes.Compare(akey, bkey) == 0
286 }
287
288 // Allowed returns a runes.Set containing every rune that is a member of the
289 // underlying profile's string class and not disallowed by any profile specific
290 // rules.
291 func (p *Profile) Allowed() runes.Set {
292         if p.options.disallow != nil {
293                 return runes.Predicate(func(r rune) bool {
294                         return p.class.Contains(r) && !p.options.disallow.Contains(r)
295                 })
296         }
297         return p.class
298 }
299
300 type checker struct {
301         p       *Profile
302         allowed runes.Set
303
304         beforeBits catBitmap
305         termBits   catBitmap
306         acceptBits catBitmap
307 }
308
309 func (c *checker) Reset() {
310         c.beforeBits = 0
311         c.termBits = 0
312         c.acceptBits = 0
313 }
314
315 func (c *checker) span(src []byte, atEOF bool) (n int, err error) {
316         for n < len(src) {
317                 e, sz := dpTrie.lookup(src[n:])
318                 d := categoryTransitions[category(e&catMask)]
319                 if sz == 0 {
320                         if !atEOF {
321                                 return n, transform.ErrShortSrc
322                         }
323                         return n, errDisallowedRune
324                 }
325                 doLookAhead := false
326                 if property(e) < c.p.class.validFrom {
327                         if d.rule == nil {
328                                 return n, errDisallowedRune
329                         }
330                         doLookAhead, err = d.rule(c.beforeBits)
331                         if err != nil {
332                                 return n, err
333                         }
334                 }
335                 c.beforeBits &= d.keep
336                 c.beforeBits |= d.set
337                 if c.termBits != 0 {
338                         // We are currently in an unterminated lookahead.
339                         if c.beforeBits&c.termBits != 0 {
340                                 c.termBits = 0
341                                 c.acceptBits = 0
342                         } else if c.beforeBits&c.acceptBits == 0 {
343                                 // Invalid continuation of the unterminated lookahead sequence.
344                                 return n, errContext
345                         }
346                 }
347                 if doLookAhead {
348                         if c.termBits != 0 {
349                                 // A previous lookahead run has not been terminated yet.
350                                 return n, errContext
351                         }
352                         c.termBits = d.term
353                         c.acceptBits = d.accept
354                 }
355                 n += sz
356         }
357         if m := c.beforeBits >> finalShift; c.beforeBits&m != m || c.termBits != 0 {
358                 err = errContext
359         }
360         return n, err
361 }
362
363 // TODO: we may get rid of this transform if transform.Chain understands
364 // something like a Spanner interface.
365 func (c checker) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
366         short := false
367         if len(dst) < len(src) {
368                 src = src[:len(dst)]
369                 atEOF = false
370                 short = true
371         }
372         nSrc, err = c.span(src, atEOF)
373         nDst = copy(dst, src[:nSrc])
374         if short && (err == transform.ErrShortSrc || err == nil) {
375                 err = transform.ErrShortDst
376         }
377         return nDst, nSrc, err
378 }