Compress output of alternative names and emails

This commit is contained in:
Cedric Staub
2017-03-10 17:00:01 -08:00
parent 9d4a46981e
commit 5a26bbac2a
17 changed files with 2881 additions and 336 deletions
+31 -16
View File
@@ -28,6 +28,7 @@ import (
"crypto/x509/pkix"
"github.com/Masterminds/sprig"
"github.com/fatih/color"
)
@@ -55,13 +56,17 @@ Name Constraints {{if .PermittedDNSDomains.Critical}}(critical){{end}}: {{range
Key Usage:{{range .KeyUsage | keyUsage}}
{{.}}{{end}}{{end}}{{if .ExtKeyUsage}}
Extended Key Usage:{{range .ExtKeyUsage}}
{{. | extKeyUsage}}{{end}}{{end}}{{if .AltDNSNames}}
Alternate DNS Names:{{range .AltDNSNames}}
{{.}}{{end}}{{end}}{{if .AltIPAddresses}}
Alternate IP Addresses:{{range .AltIPAddresses}}
{{.}}{{end}}{{end}}{{if .EmailAddresses}}
Email Addresses:{{range .EmailAddresses}}
{{.}}{{end}}{{end}}{{if .Warnings}}
{{. | extKeyUsage}}{{end}}{{end}}
{{- if .AltDNSNames}}
Alternate DNS Names:
{{wrapWith .Width "\n\t" (join ", " .AltDNSNames)}}{{end}}
{{- if .AltIPAddresses}}
Alternate IP Addresses:
{{wrapWith .Width "\n\t" (join ", " .AltIPAddresses)}}{{end}}
{{- if .EmailAddresses}}
Email Addresses:
{{wrapWith .Width "\n\t" (join ", " .EmailAddresses)}}{{end}}
{{- if .Warnings}}
Warnings:{{range .Warnings}}
{{. | redify}}{{end}}{{end}}`
@@ -75,14 +80,14 @@ Issuer: {{.Issuer.Name | printShortName }}
Name Constraints{{if .PermittedDNSDomains.Critical}} (critical){{end}}: {{range .NameConstraints.PermittedDNSDomains}}
{{.}}{{end}}{{end}}
{{- if .AltDNSNames}}
Alternate DNS Names:{{range .AltDNSNames}}
{{.}}{{end}}{{end}}
Alternate DNS Names:
{{wrapWith .Width "\n\t" (join ", " .AltDNSNames)}}{{end}}
{{- if .AltIPAddresses}}
Alternate IP Addresses:{{range .AltIPAddresses}}
{{.}}{{end}}{{end}}
Alternate IP Addresses:
{{wrapWith .Width "\n\t" (join ", " .AltIPAddresses)}}{{end}}
{{- if .EmailAddresses}}
Email Addresses:{{range .EmailAddresses}}
{{.}}{{end}}{{end}}
Email Addresses:
{{wrapWith .Width "\n\t" (join ", " .EmailAddresses)}}{{end}}
{{- if .Warnings}}
Warnings:{{range .Warnings}}
{{. | redify}}{{end}}{{end}}`
@@ -131,8 +136,11 @@ func EncodeX509ToObject(cert *x509.Certificate) interface{} {
}
// EncodeX509ToText encodes an X.509 certificate into human-readable text.
func EncodeX509ToText(cert *x509.Certificate, verbose bool) []byte {
return displayCert(createSimpleCertificate("", cert), verbose)
func EncodeX509ToText(cert *x509.Certificate, terminalWidth int, verbose bool) []byte {
c := createSimpleCertificate("", cert)
c.Width = terminalWidth - 8 /* Need some margin for tab */
return displayCert(c, verbose)
}
// displayCert takes in a parsed certificate object
@@ -140,7 +148,10 @@ func EncodeX509ToText(cert *x509.Certificate, verbose bool) []byte {
// information. Start and end dates are colored based on whether or not
// the certificate is expired, not expired, or close to expiring.
func displayCert(cert simpleCertificate, verbose bool) []byte {
funcMap := template.FuncMap{
// Use template functions from sprig, but add some extras
funcMap := sprig.TxtFuncMap()
extras := template.FuncMap{
"certStart": certStart,
"certEnd": certEnd,
"redify": redify,
@@ -152,6 +163,10 @@ func displayCert(cert simpleCertificate, verbose bool) []byte {
"oidShort": oidShort,
"printShortName": printShortName,
}
for k, v := range extras {
funcMap[k] = v
}
t := template.New("Cert template").Funcs(funcMap)
var err error
if verbose {
+3
View File
@@ -116,6 +116,9 @@ type simpleCertificate struct {
EmailAddresses []string `json:"email_addresses,omitempty"`
Warnings []string `json:"warnings,omitempty"`
PEM string `json:"pem,omitempty"`
// Internal fields for text display. Set - to skip serialize.
Width int `json:"-"`
}
type simplePKIXName struct {
+20 -2
View File
@@ -63,9 +63,27 @@ var (
verifyJSON = verify.Flag("json", "Write output as machine-readable JSON format.").Short('j').Bool()
)
const defaultWidth = 60
const maxWidth = 80
func main() {
app.Version("1.7.0")
var terminalWidth int
fd := int(os.Stdout.Fd())
if terminal.IsTerminal(fd) {
var err error
terminalWidth, _, err = terminal.GetSize(fd)
if err != nil {
terminalWidth = defaultWidth
}
} else {
terminalWidth = defaultWidth
}
if terminalWidth > maxWidth {
terminalWidth = maxWidth
}
stdout := colorable.NewColorableStdout()
result := simpleResult{}
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
@@ -93,7 +111,7 @@ func main() {
} else {
for i, cert := range result.Certificates {
fmt.Fprintf(stdout, "** CERTIFICATE %d **\n", i+1)
fmt.Fprintf(stdout, "%s\n\n", lib.EncodeX509ToText(cert, *verbose))
fmt.Fprintf(stdout, "%s\n\n", lib.EncodeX509ToText(cert, terminalWidth, *verbose))
}
}
}
@@ -131,7 +149,7 @@ func main() {
fmt.Fprintf(stdout, "%s\n\n", lib.EncodeTLSToText(result.TLSConnectionState))
for i, cert := range result.Certificates {
fmt.Fprintf(stdout, "** CERTIFICATE %d **\n", i+1)
fmt.Fprintf(stdout, "%s\n\n", lib.EncodeX509ToText(cert, *verbose))
fmt.Fprintf(stdout, "%s\n\n", lib.EncodeX509ToText(cert, terminalWidth, *verbose))
}
printVerifyResult(stdout, *result.VerifyResult)
}
+20
View File
@@ -0,0 +1,20 @@
Sprig
Copyright (C) 2013 Masterminds
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+1146
View File
File diff suppressed because it is too large Load Diff
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+259
View File
@@ -0,0 +1,259 @@
/*
Copyright 2014 Alexander Okoli
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package goutils
import (
"fmt"
"unicode"
"math"
"math/rand"
"time"
)
// Provides the time-based seed used to generate random #s
var RANDOM = rand.New(rand.NewSource(time.Now().UnixNano()))
/*
RandomNonAlphaNumeric creates a random string whose length is the number of characters specified.
Characters will be chosen from the set of all characters (ASCII/Unicode values between 0 to 2,147,483,647 (math.MaxInt32)).
Parameter:
count - the length of random string to create
Returns:
string - the random string
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
*/
func RandomNonAlphaNumeric (count int) (string, error) {
return RandomAlphaNumericCustom(count, false, false)
}
/*
RandomAscii creates a random string whose length is the number of characters specified.
Characters will be chosen from the set of characters whose ASCII value is between 32 and 126 (inclusive).
Parameter:
count - the length of random string to create
Returns:
string - the random string
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
*/
func RandomAscii(count int) (string, error) {
return Random(count, 32, 127, false, false)
}
/*
RandomNumeric creates a random string whose length is the number of characters specified.
Characters will be chosen from the set of numeric characters.
Parameter:
count - the length of random string to create
Returns:
string - the random string
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
*/
func RandomNumeric (count int) (string, error) {
return Random(count, 0, 0, false, true)
}
/*
RandomAlphabetic creates a random string whose length is the number of characters specified.
Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
Parameters:
count - the length of random string to create
letters - if true, generated string may include alphabetic characters
numbers - if true, generated string may include numeric characters
Returns:
string - the random string
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
*/
func RandomAlphabetic (count int) (string, error) {
return Random(count, 0, 0, true, false)
}
/*
RandomAlphaNumeric creates a random string whose length is the number of characters specified.
Characters will be chosen from the set of alpha-numeric characters.
Parameter:
count - the length of random string to create
Returns:
string - the random string
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
*/
func RandomAlphaNumeric (count int) (string, error) {
return Random(count, 0, 0, true, true)
}
/*
RandomAlphaNumericCustom creates a random string whose length is the number of characters specified.
Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
Parameters:
count - the length of random string to create
letters - if true, generated string may include alphabetic characters
numbers - if true, generated string may include numeric characters
Returns:
string - the random string
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
*/
func RandomAlphaNumericCustom (count int, letters bool, numbers bool) (string, error) {
return Random(count, 0, 0, letters, numbers)
}
/*
Random creates a random string based on a variety of options, using default source of randomness.
This method has exactly the same semantics as RandomSeed(int, int, int, bool, bool, []char, *rand.Rand), but
instead of using an externally supplied source of randomness, it uses the internal *rand.Rand instance.
Parameters:
count - the length of random string to create
start - the position in set of chars (ASCII/Unicode int) to start at
end - the position in set of chars (ASCII/Unicode int) to end before
letters - if true, generated string may include alphabetic characters
numbers - if true, generated string may include numeric characters
chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
Returns:
string - the random string
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
*/
func Random (count int, start int, end int, letters bool, numbers bool, chars ...rune) (string, error) {
return RandomSeed (count, start, end, letters, numbers, chars, RANDOM)
}
/*
RandomSeed creates a random string based on a variety of options, using supplied source of randomness.
If the parameters start and end are both 0, start and end are set to ' ' and 'z', the ASCII printable characters, will be used,
unless letters and numbers are both false, in which case, start and end are set to 0 and math.MaxInt32, respectively.
If chars is not nil, characters stored in chars that are between start and end are chosen.
This method accepts a user-supplied *rand.Rand instance to use as a source of randomness. By seeding a single *rand.Rand instance
with a fixed seed and using it for each call, the same random sequence of strings can be generated repeatedly and predictably.
Parameters:
count - the length of random string to create
start - the position in set of chars (ASCII/Unicode decimals) to start at
end - the position in set of chars (ASCII/Unicode decimals) to end before
letters - if true, generated string may include alphabetic characters
numbers - if true, generated string may include numeric characters
chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
random - a source of randomness.
Returns:
string - the random string
error - an error stemming from invalid parameters: if count < 0; or the provided chars array is empty; or end <= start; or end > len(chars)
*/
func RandomSeed (count int, start int, end int, letters bool, numbers bool, chars []rune, random *rand.Rand) (string, error) {
if count == 0 {
return "", nil
} else if count < 0 {
err := fmt.Errorf("randomstringutils illegal argument: Requested random string length %v is less than 0.", count) // equiv to err := errors.New("...")
return "", err
}
if chars != nil && len(chars) == 0 {
err := fmt.Errorf("randomstringutils illegal argument: The chars array must not be empty")
return "", err
}
if start == 0 && end == 0 {
if chars != nil {
end = len(chars)
} else {
if !letters && !numbers {
end = math.MaxInt32
} else {
end = 'z' + 1
start = ' '
}
}
} else {
if end <= start {
err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) must be greater than start (%v)", end, start)
return "", err
}
if chars != nil && end > len(chars) {
err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) cannot be greater than len(chars) (%v)", end, len(chars))
return "", err
}
}
buffer := make([]rune, count)
gap := end - start
// high-surrogates range, (\uD800-\uDBFF) = 55296 - 56319
// low-surrogates range, (\uDC00-\uDFFF) = 56320 - 57343
for count != 0 {
count--
var ch rune
if chars == nil {
ch = rune(random.Intn(gap) + start)
} else {
ch = chars[random.Intn(gap) + start]
}
if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {
if ch >= 56320 && ch <= 57343 { // low surrogate range
if count == 0 {
count++
} else {
// Insert low surrogate
buffer[count] = ch
count--
// Insert high surrogate
buffer[count] = rune(55296 + random.Intn(128))
}
} else if ch >= 55296 && ch <= 56191 { // High surrogates range (Partial)
if count == 0 {
count++
} else {
// Insert low surrogate
buffer[count] = rune(56320 + random.Intn(128))
count--
// Insert high surrogate
buffer[count] = ch
}
} else if ch >= 56192 && ch <= 56319 {
// private high surrogate, skip it
count++
} else {
// not one of the surrogates*
buffer[count] = ch
}
} else {
count++
}
}
return string(buffer), nil
}
+232
View File
@@ -0,0 +1,232 @@
/*
Copyright 2014 Alexander Okoli
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package goutils
import (
"fmt"
"unicode"
"bytes"
"strings"
)
// Typically returned by functions where a searched item cannot be found
const INDEX_NOT_FOUND = -1
/*
Abbreviate abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "Now is the time for..."
Specifically, the algorithm is as follows:
- If str is less than maxWidth characters long, return it.
- Else abbreviate it to (str[0:maxWidth - 3] + "...").
- If maxWidth is less than 4, return an illegal argument error.
- In no case will it return a string of length greater than maxWidth.
Parameters:
str - the string to check
maxWidth - maximum length of result string, must be at least 4
Returns:
string - abbreviated string
error - if the width is too small
*/
func Abbreviate (str string, maxWidth int) (string, error) {
return AbbreviateFull(str, 0, maxWidth)
}
/*
AbbreviateFull abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "...is the time for..."
This function works like Abbreviate(string, int), but allows you to specify a "left edge" offset. Note that this left edge is not
necessarily going to be the leftmost character in the result, or the first character following the ellipses, but it will appear
somewhere in the result.
In no case will it return a string of length greater than maxWidth.
Parameters:
str - the string to check
offset - left edge of source string
maxWidth - maximum length of result string, must be at least 4
Returns:
string - abbreviated string
error - if the width is too small
*/
func AbbreviateFull (str string, offset int, maxWidth int) (string, error) {
if str == "" {
return "", nil
}
if maxWidth < 4 {
err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width is 4")
return "", err
}
if len(str) <= maxWidth {
return str, nil
}
if offset > len(str) {
offset = len(str)
}
if len(str) - offset < (maxWidth - 3) { // 15 - 5 < 10 - 3 = 10 < 7
offset = len(str) - (maxWidth - 3)
}
abrevMarker := "..."
if offset <= 4 {
return str[0:maxWidth - 3] + abrevMarker, nil// str.substring(0, maxWidth - 3) + abrevMarker;
}
if maxWidth < 7 {
err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width with offset is 7")
return "", err
}
if (offset + maxWidth - 3) < len(str) { // 5 + (10-3) < 15 = 12 < 15
abrevStr, _ := Abbreviate(str[offset:len(str)], (maxWidth - 3))
return abrevMarker + abrevStr, nil// abrevMarker + abbreviate(str.substring(offset), maxWidth - 3);
}
return abrevMarker + str[(len(str) - (maxWidth - 3)):len(str)], nil // abrevMarker + str.substring(str.length() - (maxWidth - 3));
}
/*
DeleteWhiteSpace deletes all whitespaces from a string as defined by unicode.IsSpace(rune).
It returns the string without whitespaces.
Parameter:
str - the string to delete whitespace from, may be nil
Returns:
the string without whitespaces
*/
func DeleteWhiteSpace(str string) string {
if str == "" {
return str
}
sz := len(str)
var chs bytes.Buffer
count := 0
for i := 0; i < sz; i++ {
ch := rune(str[i])
if !unicode.IsSpace(ch) {
chs.WriteRune(ch)
count++
}
}
if count == sz {
return str
}
return chs.String()
}
/*
IndexOfDifference compares two strings, and returns the index at which the strings begin to differ.
Parameters:
str1 - the first string
str2 - the second string
Returns:
the index where str1 and str2 begin to differ; -1 if they are equal
*/
func IndexOfDifference(str1 string, str2 string) int {
if str1 == str2 {
return INDEX_NOT_FOUND
}
if IsEmpty(str1) || IsEmpty(str2) {
return 0
}
var i int;
for i = 0; i < len(str1) && i < len(str2); i++ {
if rune(str1[i]) != rune(str2[i]) {
break
}
}
if i < len(str2) || i < len(str1) {
return i
}
return INDEX_NOT_FOUND
}
/*
IsBlank checks if a string is whitespace or empty (""). Observe the following behavior:
goutils.IsBlank("") = true
goutils.IsBlank(" ") = true
goutils.IsBlank("bob") = false
goutils.IsBlank(" bob ") = false
Parameter:
str - the string to check
Returns:
true - if the string is whitespace or empty ("")
*/
func IsBlank(str string) bool {
strLen := len(str)
if str == "" || strLen == 0 {
return true
}
for i := 0; i < strLen; i++ {
if unicode.IsSpace(rune(str[i])) == false {
return false
}
}
return true
}
/*
IndexOf returns the index of the first instance of sub in str, with the search beginning from the
index start point specified. -1 is returned if sub is not present in str.
An empty string ("") will return -1 (INDEX_NOT_FOUND). A negative start position is treated as zero.
A start position greater than the string length returns -1.
Parameters:
str - the string to check
sub - the substring to find
start - the start position; negative treated as zero
Returns:
the first index where the sub string was found (always >= start)
*/
func IndexOf(str string, sub string, start int) int {
if (start < 0) {
start = 0
}
if len(str) < start {
return INDEX_NOT_FOUND
}
if IsEmpty(str) || IsEmpty(sub) {
return INDEX_NOT_FOUND
}
partialIndex := strings.Index(str[start:len(str)], sub)
if partialIndex == -1 {
return INDEX_NOT_FOUND
}
return partialIndex + start
}
// IsEmpty checks if a string is empty (""). Returns true if empty, and false otherwise.
func IsEmpty(str string) bool {
return len(str) == 0
}
+365
View File
@@ -0,0 +1,365 @@
/*
Copyright 2014 Alexander Okoli
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package goutils provides utility functions to manipulate strings in various ways.
The code snippets below show examples of how to use goutils. Some functions return
errors while others do not, so usage would vary as a result.
Example:
package main
import (
"fmt"
"github.com/aokoli/goutils"
)
func main() {
// EXAMPLE 1: A goutils function which returns no errors
fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF"
// EXAMPLE 2: A goutils function which returns an error
rand1, err1 := goutils.Random (-1, 0, 0, true, true)
if err1 != nil {
fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...)
} else {
fmt.Println(rand1)
}
}
*/
package goutils
import (
"bytes"
"strings"
"unicode"
)
// VERSION indicates the current version of goutils
const VERSION = "1.0.0"
/*
Wrap wraps a single line of text, identifying words by ' '.
New lines will be separated by '\n'. Very long words, such as URLs will not be wrapped.
Leading spaces on a new line are stripped. Trailing spaces are not stripped.
Parameters:
str - the string to be word wrapped
wrapLength - the column (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
Returns:
a line with newlines inserted
*/
func Wrap (str string, wrapLength int) string {
return WrapCustom (str, wrapLength, "", false)
}
/*
WrapCustom wraps a single line of text, identifying words by ' '.
Leading spaces on a new line are stripped. Trailing spaces are not stripped.
Parameters:
str - the string to be word wrapped
wrapLength - the column number (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
newLineStr - the string to insert for a new line, "" uses '\n'
wrapLongWords - true if long words (such as URLs) should be wrapped
Returns:
a line with newlines inserted
*/
func WrapCustom (str string, wrapLength int, newLineStr string, wrapLongWords bool) string {
if str == "" {
return ""
}
if newLineStr == "" {
newLineStr = "\n" // TODO Assumes "\n" is seperator. Explore SystemUtils.LINE_SEPARATOR from Apache Commons
}
if wrapLength < 1 {
wrapLength = 1
}
inputLineLength := len(str)
offset := 0
var wrappedLine bytes.Buffer
for inputLineLength-offset > wrapLength {
if rune(str[offset]) == ' ' {
offset++
continue
}
end := wrapLength + offset + 1
spaceToWrapAt := strings.LastIndex(str[offset:end], " ") + offset
if spaceToWrapAt >= offset {
// normal word (not longer than wrapLength)
wrappedLine.WriteString(str[offset:spaceToWrapAt])
wrappedLine.WriteString(newLineStr)
offset = spaceToWrapAt + 1
} else {
// long word or URL
if wrapLongWords {
end := wrapLength + offset
// long words are wrapped one line at a time
wrappedLine.WriteString(str[offset:end])
wrappedLine.WriteString(newLineStr)
offset += wrapLength
} else {
// long words aren't wrapped, just extended beyond limit
end := wrapLength + offset
spaceToWrapAt = strings.IndexRune(str[end:len(str)], ' ') + end
if spaceToWrapAt >= 0 {
wrappedLine.WriteString(str[offset:spaceToWrapAt])
wrappedLine.WriteString(newLineStr)
offset = spaceToWrapAt + 1
} else {
wrappedLine.WriteString(str[offset:len(str)])
offset = inputLineLength
}
}
}
}
wrappedLine.WriteString(str[offset:len(str)])
return wrappedLine.String()
}
/*
Capitalize capitalizes all the delimiter separated words in a string. Only the first letter of each word is changed.
To convert the rest of each word to lowercase at the same time, use CapitalizeFully(str string, delimiters ...rune).
The delimiters represent a set of characters understood to separate words. The first string character
and the first non-delimiter character after a delimiter will be capitalized. A "" input string returns "".
Capitalization uses the Unicode title case, normally equivalent to upper case.
Parameters:
str - the string to capitalize
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
Returns:
capitalized string
*/
func Capitalize (str string, delimiters ...rune) string {
var delimLen int
if delimiters == nil {
delimLen = -1
} else {
delimLen = len(delimiters)
}
if str == "" || delimLen == 0 {
return str;
}
buffer := []rune(str)
capitalizeNext := true
for i := 0; i < len(buffer); i++ {
ch := buffer[i]
if isDelimiter(ch, delimiters...) {
capitalizeNext = true
} else if capitalizeNext {
buffer[i] = unicode.ToTitle(ch)
capitalizeNext = false
}
}
return string(buffer)
}
/*
CapitalizeFully converts all the delimiter separated words in a string into capitalized words, that is each word is made up of a
titlecase character and then a series of lowercase characters. The delimiters represent a set of characters understood
to separate words. The first string character and the first non-delimiter character after a delimiter will be capitalized.
Capitalization uses the Unicode title case, normally equivalent to upper case.
Parameters:
str - the string to capitalize fully
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
Returns:
capitalized string
*/
func CapitalizeFully (str string, delimiters ...rune) string {
var delimLen int
if delimiters == nil {
delimLen = -1
} else {
delimLen = len(delimiters)
}
if str == "" || delimLen == 0 {
return str;
}
str = strings.ToLower(str)
return Capitalize(str, delimiters...);
}
/*
Uncapitalize uncapitalizes all the whitespace separated words in a string. Only the first letter of each word is changed.
The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter
character after a delimiter will be uncapitalized. Whitespace is defined by unicode.IsSpace(char).
Parameters:
str - the string to uncapitalize fully
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
Returns:
uncapitalized string
*/
func Uncapitalize (str string, delimiters ...rune) string {
var delimLen int
if delimiters == nil {
delimLen = -1
} else {
delimLen = len(delimiters)
}
if str == "" || delimLen == 0 {
return str;
}
buffer := []rune(str)
uncapitalizeNext := true // TODO Always makes capitalize/un apply to first char.
for i := 0; i < len(buffer); i++ {
ch := buffer[i]
if isDelimiter(ch, delimiters...) {
uncapitalizeNext = true
} else if uncapitalizeNext {
buffer[i] = unicode.ToLower(ch)
uncapitalizeNext = false
}
}
return string(buffer)
}
/*
SwapCase swaps the case of a string using a word based algorithm.
Conversion algorithm:
Upper case character converts to Lower case
Title case character converts to Lower case
Lower case character after Whitespace or at start converts to Title case
Other Lower case character converts to Upper case
Whitespace is defined by unicode.IsSpace(char).
Parameters:
str - the string to swap case
Returns:
the changed string
*/
func SwapCase(str string) string {
if str == "" {
return str
}
buffer := []rune(str)
whitespace := true
for i := 0; i < len(buffer); i++ {
ch := buffer[i]
if unicode.IsUpper(ch) {
buffer[i] = unicode.ToLower(ch)
whitespace = false
} else if unicode.IsTitle(ch) {
buffer[i] = unicode.ToLower(ch)
whitespace = false
} else if unicode.IsLower(ch) {
if whitespace {
buffer[i] = unicode.ToTitle(ch)
whitespace = false
} else {
buffer[i] = unicode.ToUpper(ch)
}
} else {
whitespace = unicode.IsSpace(ch)
}
}
return string(buffer);
}
/*
Initials extracts the initial letters from each word in the string. The first letter of the string and all first
letters after the defined delimiters are returned as a new string. Their case is not changed. If the delimiters
parameter is excluded, then Whitespace is used. Whitespace is defined by unicode.IsSpacea(char). An empty delimiter array returns an empty string.
Parameters:
str - the string to get initials from
delimiters - set of characters to determine words, exclusion of this parameter means whitespace would be delimeter
Returns:
string of initial letters
*/
func Initials(str string, delimiters ...rune) string {
if str == "" {
return str
}
if delimiters != nil && len(delimiters) == 0 {
return ""
}
strLen := len(str)
var buf bytes.Buffer
lastWasGap := true
for i := 0; i < strLen; i++ {
ch := rune(str[i])
if isDelimiter(ch, delimiters...) {
lastWasGap = true
} else if lastWasGap {
buf.WriteRune(ch)
lastWasGap = false
}
}
return buf.String()
}
// private function (lower case func name)
func isDelimiter(ch rune, delimiters ...rune) bool {
if delimiters == nil {
return unicode.IsSpace(ch)
}
for _, delimiter := range delimiters {
if ch == delimiter {
return true
}
}
return false
}
+20
View File
@@ -0,0 +1,20 @@
Copyright (C) 2013-2016 by Maxim Bublis <b@codemonkey.ru>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+481
View File
@@ -0,0 +1,481 @@
// Copyright (C) 2013-2015 by Maxim Bublis <b@codemonkey.ru>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Package uuid provides implementation of Universally Unique Identifier (UUID).
// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and
// version 2 (as specified in DCE 1.1).
package uuid
import (
"bytes"
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"database/sql/driver"
"encoding/binary"
"encoding/hex"
"fmt"
"hash"
"net"
"os"
"sync"
"time"
)
// UUID layout variants.
const (
VariantNCS = iota
VariantRFC4122
VariantMicrosoft
VariantFuture
)
// UUID DCE domains.
const (
DomainPerson = iota
DomainGroup
DomainOrg
)
// Difference in 100-nanosecond intervals between
// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
const epochStart = 122192928000000000
// Used in string method conversion
const dash byte = '-'
// UUID v1/v2 storage.
var (
storageMutex sync.Mutex
storageOnce sync.Once
epochFunc = unixTimeFunc
clockSequence uint16
lastTime uint64
hardwareAddr [6]byte
posixUID = uint32(os.Getuid())
posixGID = uint32(os.Getgid())
)
// String parse helpers.
var (
urnPrefix = []byte("urn:uuid:")
byteGroups = []int{8, 4, 4, 4, 12}
)
func initClockSequence() {
buf := make([]byte, 2)
safeRandom(buf)
clockSequence = binary.BigEndian.Uint16(buf)
}
func initHardwareAddr() {
interfaces, err := net.Interfaces()
if err == nil {
for _, iface := range interfaces {
if len(iface.HardwareAddr) >= 6 {
copy(hardwareAddr[:], iface.HardwareAddr)
return
}
}
}
// Initialize hardwareAddr randomly in case
// of real network interfaces absence
safeRandom(hardwareAddr[:])
// Set multicast bit as recommended in RFC 4122
hardwareAddr[0] |= 0x01
}
func initStorage() {
initClockSequence()
initHardwareAddr()
}
func safeRandom(dest []byte) {
if _, err := rand.Read(dest); err != nil {
panic(err)
}
}
// Returns difference in 100-nanosecond intervals between
// UUID epoch (October 15, 1582) and current time.
// This is default epoch calculation function.
func unixTimeFunc() uint64 {
return epochStart + uint64(time.Now().UnixNano()/100)
}
// UUID representation compliant with specification
// described in RFC 4122.
type UUID [16]byte
// NullUUID can be used with the standard sql package to represent a
// UUID value that can be NULL in the database
type NullUUID struct {
UUID UUID
Valid bool
}
// The nil UUID is special form of UUID that is specified to have all
// 128 bits set to zero.
var Nil = UUID{}
// Predefined namespace UUIDs.
var (
NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
)
// And returns result of binary AND of two UUIDs.
func And(u1 UUID, u2 UUID) UUID {
u := UUID{}
for i := 0; i < 16; i++ {
u[i] = u1[i] & u2[i]
}
return u
}
// Or returns result of binary OR of two UUIDs.
func Or(u1 UUID, u2 UUID) UUID {
u := UUID{}
for i := 0; i < 16; i++ {
u[i] = u1[i] | u2[i]
}
return u
}
// Equal returns true if u1 and u2 equals, otherwise returns false.
func Equal(u1 UUID, u2 UUID) bool {
return bytes.Equal(u1[:], u2[:])
}
// Version returns algorithm version used to generate UUID.
func (u UUID) Version() uint {
return uint(u[6] >> 4)
}
// Variant returns UUID layout variant.
func (u UUID) Variant() uint {
switch {
case (u[8] & 0x80) == 0x00:
return VariantNCS
case (u[8]&0xc0)|0x80 == 0x80:
return VariantRFC4122
case (u[8]&0xe0)|0xc0 == 0xc0:
return VariantMicrosoft
}
return VariantFuture
}
// Bytes returns bytes slice representation of UUID.
func (u UUID) Bytes() []byte {
return u[:]
}
// Returns canonical string representation of UUID:
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
func (u UUID) String() string {
buf := make([]byte, 36)
hex.Encode(buf[0:8], u[0:4])
buf[8] = dash
hex.Encode(buf[9:13], u[4:6])
buf[13] = dash
hex.Encode(buf[14:18], u[6:8])
buf[18] = dash
hex.Encode(buf[19:23], u[8:10])
buf[23] = dash
hex.Encode(buf[24:], u[10:])
return string(buf)
}
// SetVersion sets version bits.
func (u *UUID) SetVersion(v byte) {
u[6] = (u[6] & 0x0f) | (v << 4)
}
// SetVariant sets variant bits as described in RFC 4122.
func (u *UUID) SetVariant() {
u[8] = (u[8] & 0xbf) | 0x80
}
// MarshalText implements the encoding.TextMarshaler interface.
// The encoding is the same as returned by String.
func (u UUID) MarshalText() (text []byte, err error) {
text = []byte(u.String())
return
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// Following formats are supported:
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
func (u *UUID) UnmarshalText(text []byte) (err error) {
if len(text) < 32 {
err = fmt.Errorf("uuid: UUID string too short: %s", text)
return
}
t := text[:]
braced := false
if bytes.Equal(t[:9], urnPrefix) {
t = t[9:]
} else if t[0] == '{' {
braced = true
t = t[1:]
}
b := u[:]
for i, byteGroup := range byteGroups {
if i > 0 {
if t[0] != '-' {
err = fmt.Errorf("uuid: invalid string format")
return
}
t = t[1:]
}
if len(t) < byteGroup {
err = fmt.Errorf("uuid: UUID string too short: %s", text)
return
}
if i == 4 && len(t) > byteGroup &&
((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) {
err = fmt.Errorf("uuid: UUID string too long: %s", text)
return
}
_, err = hex.Decode(b[:byteGroup/2], t[:byteGroup])
if err != nil {
return
}
t = t[byteGroup:]
b = b[byteGroup/2:]
}
return
}
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (u UUID) MarshalBinary() (data []byte, err error) {
data = u.Bytes()
return
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
// It will return error if the slice isn't 16 bytes long.
func (u *UUID) UnmarshalBinary(data []byte) (err error) {
if len(data) != 16 {
err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
return
}
copy(u[:], data)
return
}
// Value implements the driver.Valuer interface.
func (u UUID) Value() (driver.Value, error) {
return u.String(), nil
}
// Scan implements the sql.Scanner interface.
// A 16-byte slice is handled by UnmarshalBinary, while
// a longer byte slice or a string is handled by UnmarshalText.
func (u *UUID) Scan(src interface{}) error {
switch src := src.(type) {
case []byte:
if len(src) == 16 {
return u.UnmarshalBinary(src)
}
return u.UnmarshalText(src)
case string:
return u.UnmarshalText([]byte(src))
}
return fmt.Errorf("uuid: cannot convert %T to UUID", src)
}
// Value implements the driver.Valuer interface.
func (u NullUUID) Value() (driver.Value, error) {
if !u.Valid {
return nil, nil
}
// Delegate to UUID Value function
return u.UUID.Value()
}
// Scan implements the sql.Scanner interface.
func (u *NullUUID) Scan(src interface{}) error {
if src == nil {
u.UUID, u.Valid = Nil, false
return nil
}
// Delegate to UUID Scan function
u.Valid = true
return u.UUID.Scan(src)
}
// FromBytes returns UUID converted from raw byte slice input.
// It will return error if the slice isn't 16 bytes long.
func FromBytes(input []byte) (u UUID, err error) {
err = u.UnmarshalBinary(input)
return
}
// FromBytesOrNil returns UUID converted from raw byte slice input.
// Same behavior as FromBytes, but returns a Nil UUID on error.
func FromBytesOrNil(input []byte) UUID {
uuid, err := FromBytes(input)
if err != nil {
return Nil
}
return uuid
}
// FromString returns UUID parsed from string input.
// Input is expected in a form accepted by UnmarshalText.
func FromString(input string) (u UUID, err error) {
err = u.UnmarshalText([]byte(input))
return
}
// FromStringOrNil returns UUID parsed from string input.
// Same behavior as FromString, but returns a Nil UUID on error.
func FromStringOrNil(input string) UUID {
uuid, err := FromString(input)
if err != nil {
return Nil
}
return uuid
}
// Returns UUID v1/v2 storage state.
// Returns epoch timestamp, clock sequence, and hardware address.
func getStorage() (uint64, uint16, []byte) {
storageOnce.Do(initStorage)
storageMutex.Lock()
defer storageMutex.Unlock()
timeNow := epochFunc()
// Clock changed backwards since last UUID generation.
// Should increase clock sequence.
if timeNow <= lastTime {
clockSequence++
}
lastTime = timeNow
return timeNow, clockSequence, hardwareAddr[:]
}
// NewV1 returns UUID based on current timestamp and MAC address.
func NewV1() UUID {
u := UUID{}
timeNow, clockSeq, hardwareAddr := getStorage()
binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
binary.BigEndian.PutUint16(u[8:], clockSeq)
copy(u[10:], hardwareAddr)
u.SetVersion(1)
u.SetVariant()
return u
}
// NewV2 returns DCE Security UUID based on POSIX UID/GID.
func NewV2(domain byte) UUID {
u := UUID{}
timeNow, clockSeq, hardwareAddr := getStorage()
switch domain {
case DomainPerson:
binary.BigEndian.PutUint32(u[0:], posixUID)
case DomainGroup:
binary.BigEndian.PutUint32(u[0:], posixGID)
}
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
binary.BigEndian.PutUint16(u[8:], clockSeq)
u[9] = domain
copy(u[10:], hardwareAddr)
u.SetVersion(2)
u.SetVariant()
return u
}
// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
func NewV3(ns UUID, name string) UUID {
u := newFromHash(md5.New(), ns, name)
u.SetVersion(3)
u.SetVariant()
return u
}
// NewV4 returns random generated UUID.
func NewV4() UUID {
u := UUID{}
safeRandom(u[:])
u.SetVersion(4)
u.SetVariant()
return u
}
// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
func NewV5(ns UUID, name string) UUID {
u := newFromHash(sha1.New(), ns, name)
u.SetVersion(5)
u.SetVariant()
return u
}
// Returns UUID based on hashing of namespace UUID and name.
func newFromHash(h hash.Hash, ns UUID, name string) UUID {
u := UUID{}
h.Write(ns[:])
h.Write([]byte(name))
copy(u[:], h.Sum(nil))
return u
}
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-157
View File
@@ -1,157 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pbkdf2
import (
"bytes"
"crypto/sha1"
"crypto/sha256"
"hash"
"testing"
)
type testVector struct {
password string
salt string
iter int
output []byte
}
// Test vectors from RFC 6070, http://tools.ietf.org/html/rfc6070
var sha1TestVectors = []testVector{
{
"password",
"salt",
1,
[]byte{
0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
0x2f, 0xe0, 0x37, 0xa6,
},
},
{
"password",
"salt",
2,
[]byte{
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
0xd8, 0xde, 0x89, 0x57,
},
},
{
"password",
"salt",
4096,
[]byte{
0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
0x65, 0xa4, 0x29, 0xc1,
},
},
// // This one takes too long
// {
// "password",
// "salt",
// 16777216,
// []byte{
// 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
// 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
// 0x26, 0x34, 0xe9, 0x84,
// },
// },
{
"passwordPASSWORDpassword",
"saltSALTsaltSALTsaltSALTsaltSALTsalt",
4096,
[]byte{
0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
0x38,
},
},
{
"pass\000word",
"sa\000lt",
4096,
[]byte{
0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3,
},
},
}
// Test vectors from
// http://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
var sha256TestVectors = []testVector{
{
"password",
"salt",
1,
[]byte{
0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c,
0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37,
0xa8, 0x65, 0x48, 0xc9,
},
},
{
"password",
"salt",
2,
[]byte{
0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3,
0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0,
0x2a, 0x30, 0x3f, 0x8e,
},
},
{
"password",
"salt",
4096,
[]byte{
0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41,
0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d,
0x96, 0x28, 0x93, 0xa0,
},
},
{
"passwordPASSWORDpassword",
"saltSALTsaltSALTsaltSALTsaltSALTsalt",
4096,
[]byte{
0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f,
0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf,
0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18,
0x1c,
},
},
{
"pass\000word",
"sa\000lt",
4096,
[]byte{
0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89,
0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87,
},
},
}
func testHash(t *testing.T, h func() hash.Hash, hashName string, vectors []testVector) {
for i, v := range vectors {
o := Key([]byte(v.password), []byte(v.salt), v.iter, len(v.output), h)
if !bytes.Equal(o, v.output) {
t.Errorf("%s %d: expected %x, got %x", hashName, i, v.output, o)
}
}
}
func TestWithHMACSHA1(t *testing.T) {
testHash(t, sha1.New, "SHA1", sha1TestVectors)
}
func TestWithHMACSHA256(t *testing.T) {
testHash(t, sha256.New, "SHA256", sha256TestVectors)
}
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+1 -1
View File
@@ -218,7 +218,7 @@ func smix(b []byte, r, N int, v, xy []uint32) {
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// dk := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32)
// dk, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32)
//
// The recommended parameters for interactive logins as of 2009 are N=16384,
// r=8, p=1. They should be increased as memory latency and CPU parallelism
-160
View File
@@ -1,160 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package scrypt
import (
"bytes"
"testing"
)
type testVector struct {
password string
salt string
N, r, p int
output []byte
}
var good = []testVector{
{
"password",
"salt",
2, 10, 10,
[]byte{
0x48, 0x2c, 0x85, 0x8e, 0x22, 0x90, 0x55, 0xe6, 0x2f,
0x41, 0xe0, 0xec, 0x81, 0x9a, 0x5e, 0xe1, 0x8b, 0xdb,
0x87, 0x25, 0x1a, 0x53, 0x4f, 0x75, 0xac, 0xd9, 0x5a,
0xc5, 0xe5, 0xa, 0xa1, 0x5f,
},
},
{
"password",
"salt",
16, 100, 100,
[]byte{
0x88, 0xbd, 0x5e, 0xdb, 0x52, 0xd1, 0xdd, 0x0, 0x18,
0x87, 0x72, 0xad, 0x36, 0x17, 0x12, 0x90, 0x22, 0x4e,
0x74, 0x82, 0x95, 0x25, 0xb1, 0x8d, 0x73, 0x23, 0xa5,
0x7f, 0x91, 0x96, 0x3c, 0x37,
},
},
{
"this is a long \000 password",
"and this is a long \000 salt",
16384, 8, 1,
[]byte{
0xc3, 0xf1, 0x82, 0xee, 0x2d, 0xec, 0x84, 0x6e, 0x70,
0xa6, 0x94, 0x2f, 0xb5, 0x29, 0x98, 0x5a, 0x3a, 0x09,
0x76, 0x5e, 0xf0, 0x4c, 0x61, 0x29, 0x23, 0xb1, 0x7f,
0x18, 0x55, 0x5a, 0x37, 0x07, 0x6d, 0xeb, 0x2b, 0x98,
0x30, 0xd6, 0x9d, 0xe5, 0x49, 0x26, 0x51, 0xe4, 0x50,
0x6a, 0xe5, 0x77, 0x6d, 0x96, 0xd4, 0x0f, 0x67, 0xaa,
0xee, 0x37, 0xe1, 0x77, 0x7b, 0x8a, 0xd5, 0xc3, 0x11,
0x14, 0x32, 0xbb, 0x3b, 0x6f, 0x7e, 0x12, 0x64, 0x40,
0x18, 0x79, 0xe6, 0x41, 0xae,
},
},
{
"p",
"s",
2, 1, 1,
[]byte{
0x48, 0xb0, 0xd2, 0xa8, 0xa3, 0x27, 0x26, 0x11, 0x98,
0x4c, 0x50, 0xeb, 0xd6, 0x30, 0xaf, 0x52,
},
},
{
"",
"",
16, 1, 1,
[]byte{
0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20, 0x3b,
0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97, 0xf1, 0x6b,
0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8, 0xdf, 0xdf, 0xfa,
0x3f, 0xed, 0xe2, 0x14, 0x42, 0xfc, 0xd0, 0x06, 0x9d,
0xed, 0x09, 0x48, 0xf8, 0x32, 0x6a, 0x75, 0x3a, 0x0f,
0xc8, 0x1f, 0x17, 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d,
0x36, 0x28, 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89,
0x06,
},
},
{
"password",
"NaCl",
1024, 8, 16,
[]byte{
0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, 0x78,
0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe, 0x7c, 0x6a,
0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, 0xe7, 0x73, 0x76,
0x63, 0x4b, 0x37, 0x31, 0x62, 0x2e, 0xaf, 0x30, 0xd9,
0x2e, 0x22, 0xa3, 0x88, 0x6f, 0xf1, 0x09, 0x27, 0x9d,
0x98, 0x30, 0xda, 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83,
0xee, 0x6d, 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06,
0x40,
},
},
{
"pleaseletmein", "SodiumChloride",
16384, 8, 1,
[]byte{
0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48, 0x46,
0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb, 0xfd, 0xa8,
0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e, 0xa9, 0xb5, 0x43,
0xf6, 0x54, 0x5d, 0xa1, 0xf2, 0xd5, 0x43, 0x29, 0x55,
0x61, 0x3f, 0x0f, 0xcf, 0x62, 0xd4, 0x97, 0x05, 0x24,
0x2a, 0x9a, 0xf9, 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65,
0x1e, 0x40, 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58,
0x87,
},
},
/*
// Disabled: needs 1 GiB RAM and takes too long for a simple test.
{
"pleaseletmein", "SodiumChloride",
1048576, 8, 1,
[]byte{
0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae, 0xad,
0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8, 0x81, 0xec, 0x56,
0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d, 0xab, 0xe5, 0xee,
0x98, 0x20, 0xad, 0xaa, 0x47, 0x8e, 0x56, 0xfd, 0x8f,
0x4b, 0xa5, 0xd0, 0x9f, 0xfa, 0x1c, 0x6d, 0x92, 0x7c,
0x40, 0xf4, 0xc3, 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9,
0x52, 0xfb, 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41,
0xa4,
},
},
*/
}
var bad = []testVector{
{"p", "s", 0, 1, 1, nil}, // N == 0
{"p", "s", 1, 1, 1, nil}, // N == 1
{"p", "s", 7, 8, 1, nil}, // N is not power of 2
{"p", "s", 16, maxInt / 2, maxInt / 2, nil}, // p * r too large
}
func TestKey(t *testing.T) {
for i, v := range good {
k, err := Key([]byte(v.password), []byte(v.salt), v.N, v.r, v.p, len(v.output))
if err != nil {
t.Errorf("%d: got unexpected error: %s", i, err)
}
if !bytes.Equal(k, v.output) {
t.Errorf("%d: expected %x, got %x", i, v.output, k)
}
}
for i, v := range bad {
_, err := Key([]byte(v.password), []byte(v.salt), v.N, v.r, v.p, 32)
if err == nil {
t.Errorf("%d: expected error, got nil", i)
}
}
}
func BenchmarkKey(b *testing.B) {
for i := 0; i < b.N; i++ {
Key([]byte("password"), []byte("salt"), 16384, 8, 1, 64)
}
}
+47
View File
@@ -0,0 +1,47 @@
{
"version": 0,
"dependencies": [
{
"importpath": "github.com/Masterminds/sprig",
"repository": "https://github.com/Masterminds/sprig",
"vcs": "git",
"revision": "427e90187e0902bc04b64167610f2da7ba26e0e1",
"branch": "master",
"notests": true
},
{
"importpath": "github.com/aokoli/goutils",
"repository": "https://github.com/aokoli/goutils",
"vcs": "git",
"revision": "9c37978a95bd5c709a15883b6242714ea6709e64",
"branch": "master",
"notests": true
},
{
"importpath": "github.com/satori/go.uuid",
"repository": "https://github.com/satori/go.uuid",
"vcs": "git",
"revision": "b061729afc07e77a8aa4fad0a2fd840958f1942a",
"branch": "master",
"notests": true
},
{
"importpath": "golang.org/x/crypto/pbkdf2",
"repository": "https://go.googlesource.com/crypto",
"vcs": "git",
"revision": "728b753d0135da6801d45a38e6f43ff55779c5c2",
"branch": "master",
"path": "pbkdf2",
"notests": true
},
{
"importpath": "golang.org/x/crypto/scrypt",
"repository": "https://go.googlesource.com/crypto",
"vcs": "git",
"revision": "728b753d0135da6801d45a38e6f43ff55779c5c2",
"branch": "master",
"path": "/scrypt",
"notests": true
}
]
}