vendor: updating dependencies

Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
Vincent Batts 2017-06-24 14:25:26 -04:00
parent c5b7548e35
commit 50d22c5135
428 changed files with 153917 additions and 2562 deletions

12
glide.lock generated
View file

@ -1,8 +1,14 @@
hash: 90db837f2d27de645d9f48fa1be54b4f9d83f0cb9343ce98fa61e1b55b2828b5 hash: c4cfd3ace8e3926093aacecfa0f22fbeb5b2a7cb53e0b12a48b5108f6c83d9b8
updated: 2017-01-19T17:18:33.464668206-05:00 updated: 2017-06-24T14:24:17.116476656-04:00
imports: imports:
- name: github.com/Sirupsen/logrus
version: 202f25545ea4cf9b191ff7f846df5d87c9382c2b
- name: golang.org/x/crypto - name: golang.org/x/crypto
version: b8a2a83acfe6e6770b75de42d5ff4c67596675c0 version: adbae1b6b6fb4b02448a0fc0dbbc9ba2b95b294d
subpackages: subpackages:
- ripemd160 - ripemd160
- name: golang.org/x/sys
version: c23410a886927bab8ca5e80b08af6a56faeb330d
subpackages:
- unix
testImports: [] testImports: []

View file

@ -1,8 +1,10 @@
package: github.com/vbatts/go-mtree package: github.com/vbatts/go-mtree
description: File systems verification utility and library, in likeness of mtree(8)
homepage: https://github.com/vbatts/go-mtree homepage: https://github.com/vbatts/go-mtree
license: BSD-3-Clause license: BSD-3-Clause
description: File systems verification utility and library, in likeness of mtree(8)
import: import:
- package: golang.org/x/crypto - package: golang.org/x/crypto
subpackages: subpackages:
- ripemd160 - ripemd160
- package: github.com/Sirupsen/logrus
version: ^1.0.0

1
vendor/github.com/Sirupsen/logrus/.gitignore generated vendored Normal file
View file

@ -0,0 +1 @@
logrus

13
vendor/github.com/Sirupsen/logrus/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,13 @@
language: go
go:
- 1.6.x
- 1.7.x
- 1.8.x
- tip
env:
- GOMAXPROCS=4 GORACE=halt_on_error=1
install:
- go get github.com/stretchr/testify/assert
script:
- go test -race -v .
- cd hooks/null && go test -race -v .

100
vendor/github.com/Sirupsen/logrus/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,100 @@
# 1.0.0
* Officially changed name to lower-case
* bug: colors on Windows 10 (#541)
* bug: fix race in accessing level (#512)
# 0.11.5
* feature: add writer and writerlevel to entry (#372)
# 0.11.4
* bug: fix undefined variable on solaris (#493)
# 0.11.3
* formatter: configure quoting of empty values (#484)
* formatter: configure quoting character (default is `"`) (#484)
* bug: fix not importing io correctly in non-linux environments (#481)
# 0.11.2
* bug: fix windows terminal detection (#476)
# 0.11.1
* bug: fix tty detection with custom out (#471)
# 0.11.0
* performance: Use bufferpool to allocate (#370)
* terminal: terminal detection for app-engine (#343)
* feature: exit handler (#375)
# 0.10.0
* feature: Add a test hook (#180)
* feature: `ParseLevel` is now case-insensitive (#326)
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
* performance: avoid re-allocations on `WithFields` (#335)
# 0.9.0
* logrus/text_formatter: don't emit empty msg
* logrus/hooks/airbrake: move out of main repository
* logrus/hooks/sentry: move out of main repository
* logrus/hooks/papertrail: move out of main repository
* logrus/hooks/bugsnag: move out of main repository
* logrus/core: run tests with `-race`
* logrus/core: detect TTY based on `stderr`
* logrus/core: support `WithError` on logger
* logrus/core: Solaris support
# 0.8.7
* logrus/core: fix possible race (#216)
* logrus/doc: small typo fixes and doc improvements
# 0.8.6
* hooks/raven: allow passing an initialized client
# 0.8.5
* logrus/core: revert #208
# 0.8.4
* formatter/text: fix data race (#218)
# 0.8.3
* logrus/core: fix entry log level (#208)
* logrus/core: improve performance of text formatter by 40%
* logrus/core: expose `LevelHooks` type
* logrus/core: add support for DragonflyBSD and NetBSD
* formatter/text: print structs more verbosely
# 0.8.2
* logrus: fix more Fatal family functions
# 0.8.1
* logrus: fix not exiting on `Fatalf` and `Fatalln`
# 0.8.0
* logrus: defaults to stderr instead of stdout
* hooks/sentry: add special field for `*http.Request`
* formatter/text: ignore Windows for colors
# 0.7.3
* formatter/\*: allow configuration of timestamp layout
# 0.7.2
* formatter/text: Add configuration option for time format (#158)

21
vendor/github.com/Sirupsen/logrus/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Simon Eskildsen
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.

501
vendor/github.com/Sirupsen/logrus/README.md generated vendored Normal file
View file

@ -0,0 +1,501 @@
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus)&nbsp;[![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus)
Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
many large deployments. The core API is unlikely to change much but please
version control your Logrus to make sure you aren't fetching latest `master` on
every build.**
**Seeing weird case-sensitive problems?** Unfortunately, the author failed to
realize the consequences of renaming to lower-case. Due to the Go package
environment, this caused issues. Regretfully, there's no turning back now.
Everything using `logrus` will need to use the lower-case:
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
I am terribly sorry for this inconvenience. Logrus strives hard for backwards
compatibility, and the author failed to realize the cascading consequences of
such a name-change. To fix Glide, see [these
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):
![Colored](http://i.imgur.com/PY7qMwd.png)
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
or Splunk:
```json
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!",
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
```
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
attached, the output is compatible with the
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
```text
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
exit status 1
```
#### Case-sensitivity
The organization's name was changed to lower-case--and this will not be changed
back. If you are getting import conflicts due to case sensitivity, please use
the lower-case import: `github.com/sirupsen/logrus`.
#### Example
The simplest way to use Logrus is simply the package-level exported logger:
```go
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
```
Note that it's completely api-compatible with the stdlib logger, so you can
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
and you'll now have the flexibility of Logrus. You can customize it all you
want:
```go
package main
import (
"os"
log "github.com/sirupsen/logrus"
)
func init() {
// Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{})
// Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
log.SetOutput(os.Stdout)
// Only log the warning severity or above.
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
```
For more advanced usage such as logging to multiple locations from the same
application, you can also create an instance of the `logrus` Logger:
```go
package main
import (
"os"
"github.com/sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
var log = logrus.New()
func main() {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
log.Out = os.Stdout
// You could set this to any `io.Writer` such as a file
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
// if err == nil {
// log.Out = file
// } else {
// log.Info("Failed to log to file, using default stderr")
// }
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
}
```
#### Fields
Logrus encourages careful, structured logging through logging fields instead of
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
to send event %s to topic %s with key %d")`, you should log the much more
discoverable:
```go
log.WithFields(log.Fields{
"event": event,
"topic": topic,
"key": key,
}).Fatal("Failed to send event")
```
We've found this API forces you to think about logging in a way that produces
much more useful logging messages. We've been in countless situations where just
a single added field to a log statement that was already there would've saved us
hours. The `WithFields` call is optional.
In general, with Logrus using any of the `printf`-family functions should be
seen as a hint you should add a field, however, you can still use the
`printf`-family functions with Logrus.
#### Default Fields
Often it's helpful to have fields _always_ attached to log statements in an
application or parts of one. For example, you may want to always log the
`request_id` and `user_ip` in the context of a request. Instead of writing
`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
every line, you can create a `logrus.Entry` to pass around instead:
```go
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Info("something happened on that request") # will log request_id and user_ip
requestLogger.Warn("something not great happened")
```
#### Hooks
You can add hooks for logging levels. For example to send errors to an exception
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
multiple places simultaneously, e.g. syslog.
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
`init`:
```go
import (
log "github.com/sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init() {
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
log.Error("Unable to connect to local syslog daemon")
} else {
log.AddHook(hook)
}
}
```
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
| Hook | Description |
| ----- | ----------- |
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) |
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) |
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
| [Firehose](https://github.com/beaubrewer/logrus_firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/)
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
| [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) |
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
| [Influxus](http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB](http://influxdata.com/) |
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) |
| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) |
| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
| [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) |
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) |
| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) |
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)|
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
| [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) |
| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
| [Syslog TLS](https://github.com/shinji62/logrus-syslog-ng) | Send errors to remote syslog server with TLS support. |
| [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) |
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash |
| [SQS-Hook](https://github.com/tsarpaul/logrus_sqs) | Hook for logging to [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) |
#### Level logging
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
```go
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
log.Fatal("Bye.")
// Calls panic() after logging
log.Panic("I'm bailing.")
```
You can set the logging level on a `Logger`, then it will only log entries with
that severity or anything above it:
```go
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log.SetLevel(log.InfoLevel)
```
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
environment if your application has that.
#### Entries
Besides the fields added with `WithField` or `WithFields` some fields are
automatically added to all logging events:
1. `time`. The timestamp when the entry was created.
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
the `AddFields` call. E.g. `Failed to send event.`
3. `level`. The logging level. E.g. `info`.
#### Environments
Logrus has no notion of environment.
If you wish for hooks and formatters to only be used in specific environments,
you should handle that yourself. For example, if your application has a global
variable `Environment`, which is a string representation of the environment you
could do:
```go
import (
log "github.com/sirupsen/logrus"
)
init() {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
log.SetFormatter(&log.JSONFormatter{})
} else {
// The TextFormatter is default, you don't actually have to do this.
log.SetFormatter(&log.TextFormatter{})
}
}
```
This configuration is how `logrus` was intended to be used, but JSON in
production is mostly only useful if you do log aggregation with tools like
Splunk or Logstash.
#### Formatters
The built-in logging formatters are:
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
without colors.
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
field to `true`. To force no colored output even if there is a TTY set the
`DisableColors` field to `true`. For Windows, see
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
* `logrus.JSONFormatter`. Logs fields as JSON.
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
Third party logging formatters:
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
You can define your formatter by implementing the `Formatter` interface,
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
`Fields` type (`map[string]interface{}`) with all your fields as well as the
default ones (see Entries section above):
```go
type MyJSONFormatter struct {
}
log.SetFormatter(new(MyJSONFormatter))
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
serialized, err := json.Marshal(entry.Data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}
```
#### Logger as an `io.Writer`
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
```go
w := logger.Writer()
defer w.Close()
srv := http.Server{
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog: log.New(w, "", 0),
}
```
Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is `info`.
This means that we can override the standard library logger easily:
```go
logger := logrus.New()
logger.Formatter = &logrus.JSONFormatter{}
// Use logrus for standard log output
// Note that `log` here references stdlib's log
// Not logrus imported under the name `log`.
log.SetOutput(logger.Writer())
```
#### Rotation
Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like `logrotate(8)`) that can compress and delete old log
entries. It should not be a feature of the application-level logger.
#### Tools
| Tool | Description |
| ---- | ----------- |
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
#### Testing
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
```go
import(
"github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/null"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSomething(t*testing.T){
logger, hook := null.NewNullLogger()
logger.Error("Helloerror")
assert.Equal(t, 1, len(hook.Entries))
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
hook.Reset()
assert.Nil(t, hook.LastEntry())
}
```
#### Fatal handlers
Logrus can register one or more functions that will be called when any `fatal`
level message is logged. The registered handlers will be executed before
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
```
...
handler := func() {
// gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)
...
```
#### Thread safety
By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
Situation when locking is not needed includes:
* You have no hooks registered, or hooks calling is already thread-safe.
* Writing to logger.Out is already thread-safe, for example:
1) logger.Out is protected by locks.
2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)

64
vendor/github.com/Sirupsen/logrus/alt_exit.go generated vendored Normal file
View file

@ -0,0 +1,64 @@
package logrus
// The following code was sourced and modified from the
// https://github.com/tebeka/atexit package governed by the following license:
//
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
//
// 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.
import (
"fmt"
"os"
)
var handlers = []func(){}
func runHandler(handler func()) {
defer func() {
if err := recover(); err != nil {
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
}
}()
handler()
}
func runHandlers() {
for _, handler := range handlers {
runHandler(handler)
}
}
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
func Exit(code int) {
runHandlers()
os.Exit(code)
}
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
// all handlers. The handlers will also be invoked when any Fatal log entry is
// made.
//
// This method is useful when a caller wishes to use logrus to log a fatal
// message but also needs to gracefully shutdown. An example usecase could be
// closing database connections, or sending a alert that the application is
// closing.
func RegisterExitHandler(handler func()) {
handlers = append(handlers, handler)
}

74
vendor/github.com/Sirupsen/logrus/alt_exit_test.go generated vendored Normal file
View file

@ -0,0 +1,74 @@
package logrus
import (
"io/ioutil"
"os/exec"
"testing"
"time"
)
func TestRegister(t *testing.T) {
current := len(handlers)
RegisterExitHandler(func() {})
if len(handlers) != current+1 {
t.Fatalf("can't add handler")
}
}
func TestHandler(t *testing.T) {
gofile := "/tmp/testprog.go"
if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil {
t.Fatalf("can't create go file")
}
outfile := "/tmp/testprog.out"
arg := time.Now().UTC().String()
err := exec.Command("go", "run", gofile, outfile, arg).Run()
if err == nil {
t.Fatalf("completed normally, should have failed")
}
data, err := ioutil.ReadFile(outfile)
if err != nil {
t.Fatalf("can't read output file %s", outfile)
}
if string(data) != arg {
t.Fatalf("bad data")
}
}
var testprog = []byte(`
// Test program for atexit, gets output file and data as arguments and writes
// data to output file in atexit handler.
package main
import (
"github.com/sirupsen/logrus"
"flag"
"fmt"
"io/ioutil"
)
var outfile = ""
var data = ""
func handler() {
ioutil.WriteFile(outfile, []byte(data), 0666)
}
func badHandler() {
n := 0
fmt.Println(1/n)
}
func main() {
flag.Parse()
outfile = flag.Arg(0)
data = flag.Arg(1)
logrus.RegisterExitHandler(handler)
logrus.RegisterExitHandler(badHandler)
logrus.Fatal("Bye bye")
}
`)

26
vendor/github.com/Sirupsen/logrus/doc.go generated vendored Normal file
View file

@ -0,0 +1,26 @@
/*
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
The simplest way to use Logrus is simply the package-level exported logger:
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"number": 1,
"size": 10,
}).Info("A walrus appears")
}
Output:
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
For a full guide visit https://github.com/sirupsen/logrus
*/
package logrus

275
vendor/github.com/Sirupsen/logrus/entry.go generated vendored Normal file
View file

@ -0,0 +1,275 @@
package logrus
import (
"bytes"
"fmt"
"os"
"sync"
"time"
)
var bufferPool *sync.Pool
func init() {
bufferPool = &sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
}
// Defines the key when adding errors using WithError.
var ErrorKey = "error"
// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
// passed around as much as you wish to avoid field duplication.
type Entry struct {
Logger *Logger
// Contains all the fields set by the user.
Data Fields
// Time at which the log entry was created
Time time.Time
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
Level Level
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
Message string
// When formatter is called in entry.log(), an Buffer may be set to entry
Buffer *bytes.Buffer
}
func NewEntry(logger *Logger) *Entry {
return &Entry{
Logger: logger,
// Default is three fields, give a little extra room
Data: make(Fields, 5),
}
}
// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil {
return "", err
}
str := string(serialized)
return str, nil
}
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
return entry.WithField(ErrorKey, err)
}
// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
return entry.WithFields(Fields{key: value})
}
// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
data := make(Fields, len(entry.Data)+len(fields))
for k, v := range entry.Data {
data[k] = v
}
for k, v := range fields {
data[k] = v
}
return &Entry{Logger: entry.Logger, Data: data}
}
// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {
var buffer *bytes.Buffer
entry.Time = time.Now()
entry.Level = level
entry.Message = msg
if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
entry.Logger.mu.Lock()
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
entry.Logger.mu.Unlock()
}
buffer = bufferPool.Get().(*bytes.Buffer)
buffer.Reset()
defer bufferPool.Put(buffer)
entry.Buffer = buffer
serialized, err := entry.Logger.Formatter.Format(&entry)
entry.Buffer = nil
if err != nil {
entry.Logger.mu.Lock()
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
entry.Logger.mu.Unlock()
} else {
entry.Logger.mu.Lock()
_, err = entry.Logger.Out.Write(serialized)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
}
entry.Logger.mu.Unlock()
}
// To avoid Entry#log() returning a value that only would make sense for
// panic() to use in Entry#Panic(), we avoid the allocation by checking
// directly here.
if level <= PanicLevel {
panic(&entry)
}
}
func (entry *Entry) Debug(args ...interface{}) {
if entry.Logger.level() >= DebugLevel {
entry.log(DebugLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Print(args ...interface{}) {
entry.Info(args...)
}
func (entry *Entry) Info(args ...interface{}) {
if entry.Logger.level() >= InfoLevel {
entry.log(InfoLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Warn(args ...interface{}) {
if entry.Logger.level() >= WarnLevel {
entry.log(WarnLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Warning(args ...interface{}) {
entry.Warn(args...)
}
func (entry *Entry) Error(args ...interface{}) {
if entry.Logger.level() >= ErrorLevel {
entry.log(ErrorLevel, fmt.Sprint(args...))
}
}
func (entry *Entry) Fatal(args ...interface{}) {
if entry.Logger.level() >= FatalLevel {
entry.log(FatalLevel, fmt.Sprint(args...))
}
Exit(1)
}
func (entry *Entry) Panic(args ...interface{}) {
if entry.Logger.level() >= PanicLevel {
entry.log(PanicLevel, fmt.Sprint(args...))
}
panic(fmt.Sprint(args...))
}
// Entry Printf family functions
func (entry *Entry) Debugf(format string, args ...interface{}) {
if entry.Logger.level() >= DebugLevel {
entry.Debug(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Infof(format string, args ...interface{}) {
if entry.Logger.level() >= InfoLevel {
entry.Info(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Printf(format string, args ...interface{}) {
entry.Infof(format, args...)
}
func (entry *Entry) Warnf(format string, args ...interface{}) {
if entry.Logger.level() >= WarnLevel {
entry.Warn(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Warningf(format string, args ...interface{}) {
entry.Warnf(format, args...)
}
func (entry *Entry) Errorf(format string, args ...interface{}) {
if entry.Logger.level() >= ErrorLevel {
entry.Error(fmt.Sprintf(format, args...))
}
}
func (entry *Entry) Fatalf(format string, args ...interface{}) {
if entry.Logger.level() >= FatalLevel {
entry.Fatal(fmt.Sprintf(format, args...))
}
Exit(1)
}
func (entry *Entry) Panicf(format string, args ...interface{}) {
if entry.Logger.level() >= PanicLevel {
entry.Panic(fmt.Sprintf(format, args...))
}
}
// Entry Println family functions
func (entry *Entry) Debugln(args ...interface{}) {
if entry.Logger.level() >= DebugLevel {
entry.Debug(entry.sprintlnn(args...))
}
}
func (entry *Entry) Infoln(args ...interface{}) {
if entry.Logger.level() >= InfoLevel {
entry.Info(entry.sprintlnn(args...))
}
}
func (entry *Entry) Println(args ...interface{}) {
entry.Infoln(args...)
}
func (entry *Entry) Warnln(args ...interface{}) {
if entry.Logger.level() >= WarnLevel {
entry.Warn(entry.sprintlnn(args...))
}
}
func (entry *Entry) Warningln(args ...interface{}) {
entry.Warnln(args...)
}
func (entry *Entry) Errorln(args ...interface{}) {
if entry.Logger.level() >= ErrorLevel {
entry.Error(entry.sprintlnn(args...))
}
}
func (entry *Entry) Fatalln(args ...interface{}) {
if entry.Logger.level() >= FatalLevel {
entry.Fatal(entry.sprintlnn(args...))
}
Exit(1)
}
func (entry *Entry) Panicln(args ...interface{}) {
if entry.Logger.level() >= PanicLevel {
entry.Panic(entry.sprintlnn(args...))
}
}
// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
msg := fmt.Sprintln(args...)
return msg[:len(msg)-1]
}

77
vendor/github.com/Sirupsen/logrus/entry_test.go generated vendored Normal file
View file

@ -0,0 +1,77 @@
package logrus
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestEntryWithError(t *testing.T) {
assert := assert.New(t)
defer func() {
ErrorKey = "error"
}()
err := fmt.Errorf("kaboom at layer %d", 4711)
assert.Equal(err, WithError(err).Data["error"])
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
assert.Equal(err, entry.WithError(err).Data["error"])
ErrorKey = "err"
assert.Equal(err, entry.WithError(err).Data["err"])
}
func TestEntryPanicln(t *testing.T) {
errBoom := fmt.Errorf("boom time")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicln("kaboom")
}
func TestEntryPanicf(t *testing.T) {
errBoom := fmt.Errorf("boom again")
defer func() {
p := recover()
assert.NotNil(t, p)
switch pVal := p.(type) {
case *Entry:
assert.Equal(t, "kaboom true", pVal.Message)
assert.Equal(t, errBoom, pVal.Data["err"])
default:
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
}
}()
logger := New()
logger.Out = &bytes.Buffer{}
entry := NewEntry(logger)
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
}

View file

@ -0,0 +1,59 @@
package main
import (
"github.com/sirupsen/logrus"
// "os"
)
var log = logrus.New()
func init() {
log.Formatter = new(logrus.JSONFormatter)
log.Formatter = new(logrus.TextFormatter) // default
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
// if err == nil {
// log.Out = file
// } else {
// log.Info("Failed to log to file, using default stderr")
// }
log.Level = logrus.DebugLevel
}
func main() {
defer func() {
err := recover()
if err != nil {
log.WithFields(logrus.Fields{
"omg": true,
"err": err,
"number": 100,
}).Fatal("The ice breaks!")
}
}()
log.WithFields(logrus.Fields{
"animal": "walrus",
"number": 8,
}).Debug("Started observing beach")
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(logrus.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(logrus.Fields{
"temperature": -4,
}).Debug("Temperature changes")
log.WithFields(logrus.Fields{
"animal": "orca",
"size": 9009,
}).Panic("It's over 9000!")
}

View file

@ -0,0 +1,30 @@
package main
import (
"github.com/sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
)
var log = logrus.New()
func init() {
log.Formatter = new(logrus.TextFormatter) // default
log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
}
func main() {
log.WithFields(logrus.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(logrus.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(logrus.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
}

193
vendor/github.com/Sirupsen/logrus/exported.go generated vendored Normal file
View file

@ -0,0 +1,193 @@
package logrus
import (
"io"
)
var (
// std is the name of the standard logger in stdlib `log`
std = New()
)
func StandardLogger() *Logger {
return std
}
// SetOutput sets the standard logger output.
func SetOutput(out io.Writer) {
std.mu.Lock()
defer std.mu.Unlock()
std.Out = out
}
// SetFormatter sets the standard logger formatter.
func SetFormatter(formatter Formatter) {
std.mu.Lock()
defer std.mu.Unlock()
std.Formatter = formatter
}
// SetLevel sets the standard logger level.
func SetLevel(level Level) {
std.mu.Lock()
defer std.mu.Unlock()
std.setLevel(level)
}
// GetLevel returns the standard logger level.
func GetLevel() Level {
std.mu.Lock()
defer std.mu.Unlock()
return std.level()
}
// AddHook adds a hook to the standard logger hooks.
func AddHook(hook Hook) {
std.mu.Lock()
defer std.mu.Unlock()
std.Hooks.Add(hook)
}
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
func WithError(err error) *Entry {
return std.WithField(ErrorKey, err)
}
// WithField creates an entry from the standard logger and adds a field to
// it. If you want multiple fields, use `WithFields`.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithField(key string, value interface{}) *Entry {
return std.WithField(key, value)
}
// WithFields creates an entry from the standard logger and adds multiple
// fields to it. This is simply a helper for `WithField`, invoking it
// once for each field.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithFields(fields Fields) *Entry {
return std.WithFields(fields)
}
// Debug logs a message at level Debug on the standard logger.
func Debug(args ...interface{}) {
std.Debug(args...)
}
// Print logs a message at level Info on the standard logger.
func Print(args ...interface{}) {
std.Print(args...)
}
// Info logs a message at level Info on the standard logger.
func Info(args ...interface{}) {
std.Info(args...)
}
// Warn logs a message at level Warn on the standard logger.
func Warn(args ...interface{}) {
std.Warn(args...)
}
// Warning logs a message at level Warn on the standard logger.
func Warning(args ...interface{}) {
std.Warning(args...)
}
// Error logs a message at level Error on the standard logger.
func Error(args ...interface{}) {
std.Error(args...)
}
// Panic logs a message at level Panic on the standard logger.
func Panic(args ...interface{}) {
std.Panic(args...)
}
// Fatal logs a message at level Fatal on the standard logger.
func Fatal(args ...interface{}) {
std.Fatal(args...)
}
// Debugf logs a message at level Debug on the standard logger.
func Debugf(format string, args ...interface{}) {
std.Debugf(format, args...)
}
// Printf logs a message at level Info on the standard logger.
func Printf(format string, args ...interface{}) {
std.Printf(format, args...)
}
// Infof logs a message at level Info on the standard logger.
func Infof(format string, args ...interface{}) {
std.Infof(format, args...)
}
// Warnf logs a message at level Warn on the standard logger.
func Warnf(format string, args ...interface{}) {
std.Warnf(format, args...)
}
// Warningf logs a message at level Warn on the standard logger.
func Warningf(format string, args ...interface{}) {
std.Warningf(format, args...)
}
// Errorf logs a message at level Error on the standard logger.
func Errorf(format string, args ...interface{}) {
std.Errorf(format, args...)
}
// Panicf logs a message at level Panic on the standard logger.
func Panicf(format string, args ...interface{}) {
std.Panicf(format, args...)
}
// Fatalf logs a message at level Fatal on the standard logger.
func Fatalf(format string, args ...interface{}) {
std.Fatalf(format, args...)
}
// Debugln logs a message at level Debug on the standard logger.
func Debugln(args ...interface{}) {
std.Debugln(args...)
}
// Println logs a message at level Info on the standard logger.
func Println(args ...interface{}) {
std.Println(args...)
}
// Infoln logs a message at level Info on the standard logger.
func Infoln(args ...interface{}) {
std.Infoln(args...)
}
// Warnln logs a message at level Warn on the standard logger.
func Warnln(args ...interface{}) {
std.Warnln(args...)
}
// Warningln logs a message at level Warn on the standard logger.
func Warningln(args ...interface{}) {
std.Warningln(args...)
}
// Errorln logs a message at level Error on the standard logger.
func Errorln(args ...interface{}) {
std.Errorln(args...)
}
// Panicln logs a message at level Panic on the standard logger.
func Panicln(args ...interface{}) {
std.Panicln(args...)
}
// Fatalln logs a message at level Fatal on the standard logger.
func Fatalln(args ...interface{}) {
std.Fatalln(args...)
}

45
vendor/github.com/Sirupsen/logrus/formatter.go generated vendored Normal file
View file

@ -0,0 +1,45 @@
package logrus
import "time"
const DefaultTimestampFormat = time.RFC3339
// The Formatter interface is used to implement a custom Formatter. It takes an
// `Entry`. It exposes all the fields, including the default ones:
//
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
// * `entry.Data["time"]`. The timestamp.
// * `entry.Data["level"]. The level the entry was logged at.
//
// Any additional fields added with `WithField` or `WithFields` are also in
// `entry.Data`. Format is expected to return an array of bytes which are then
// logged to `logger.Out`.
type Formatter interface {
Format(*Entry) ([]byte, error)
}
// This is to not silently overwrite `time`, `msg` and `level` fields when
// dumping it. If this code wasn't there doing:
//
// logrus.WithField("level", 1).Info("hello")
//
// Would just silently drop the user provided level. Instead with this code
// it'll logged as:
//
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
//
// It's not exported because it's still using Data in an opinionated way. It's to
// avoid code duplication between the two default formatters.
func prefixFieldClashes(data Fields) {
if t, ok := data["time"]; ok {
data["fields.time"] = t
}
if m, ok := data["msg"]; ok {
data["fields.msg"] = m
}
if l, ok := data["level"]; ok {
data["fields.level"] = l
}
}

View file

@ -0,0 +1,101 @@
package logrus
import (
"fmt"
"testing"
"time"
)
// smallFields is a small size data set for benchmarking
var smallFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
}
// largeFields is a large size data set for benchmarking
var largeFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
"five": "six",
"seven": "eight",
"nine": "ten",
"eleven": "twelve",
"thirteen": "fourteen",
"fifteen": "sixteen",
"seventeen": "eighteen",
"nineteen": "twenty",
"a": "b",
"c": "d",
"e": "f",
"g": "h",
"i": "j",
"k": "l",
"m": "n",
"o": "p",
"q": "r",
"s": "t",
"u": "v",
"w": "x",
"y": "z",
"this": "will",
"make": "thirty",
"entries": "yeah",
}
var errorFields = Fields{
"foo": fmt.Errorf("bar"),
"baz": fmt.Errorf("qux"),
}
func BenchmarkErrorTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
}
func BenchmarkSmallTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
}
func BenchmarkLargeTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
}
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
}
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
}
func BenchmarkSmallJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, smallFields)
}
func BenchmarkLargeJSONFormatter(b *testing.B) {
doBenchmark(b, &JSONFormatter{}, largeFields)
}
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
logger := New()
entry := &Entry{
Time: time.Time{},
Level: InfoLevel,
Message: "message",
Data: fields,
Logger: logger,
}
var d []byte
var err error
for i := 0; i < b.N; i++ {
d, err = formatter.Format(entry)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(d)))
}
}

122
vendor/github.com/Sirupsen/logrus/hook_test.go generated vendored Normal file
View file

@ -0,0 +1,122 @@
package logrus
import (
"testing"
"github.com/stretchr/testify/assert"
)
type TestHook struct {
Fired bool
}
func (hook *TestHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *TestHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookFires(t *testing.T) {
hook := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
assert.Equal(t, hook.Fired, false)
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}
type ModifyHook struct {
}
func (hook *ModifyHook) Fire(entry *Entry) error {
entry.Data["wow"] = "whale"
return nil
}
func (hook *ModifyHook) Levels() []Level {
return []Level{
DebugLevel,
InfoLevel,
WarnLevel,
ErrorLevel,
FatalLevel,
PanicLevel,
}
}
func TestHookCanModifyEntry(t *testing.T) {
hook := new(ModifyHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
})
}
func TestCanFireMultipleHooks(t *testing.T) {
hook1 := new(ModifyHook)
hook2 := new(TestHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook1)
log.Hooks.Add(hook2)
log.WithField("wow", "elephant").Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["wow"], "whale")
assert.Equal(t, hook2.Fired, true)
})
}
type ErrorHook struct {
Fired bool
}
func (hook *ErrorHook) Fire(entry *Entry) error {
hook.Fired = true
return nil
}
func (hook *ErrorHook) Levels() []Level {
return []Level{
ErrorLevel,
}
}
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, false)
})
}
func TestErrorHookShouldFireOnError(t *testing.T) {
hook := new(ErrorHook)
LogAndAssertJSON(t, func(log *Logger) {
log.Hooks.Add(hook)
log.Error("test")
}, func(fields Fields) {
assert.Equal(t, hook.Fired, true)
})
}

34
vendor/github.com/Sirupsen/logrus/hooks.go generated vendored Normal file
View file

@ -0,0 +1,34 @@
package logrus
// A hook to be fired when logging on the logging levels returned from
// `Levels()` on your implementation of the interface. Note that this is not
// fired in a goroutine or a channel with workers, you should handle such
// functionality yourself if your call is non-blocking and you don't wish for
// the logging calls for levels returned from `Levels()` to block.
type Hook interface {
Levels() []Level
Fire(*Entry) error
}
// Internal type for storing the hooks on a logger instance.
type LevelHooks map[Level][]Hook
// Add a hook to an instance of logger. This is called with
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
func (hooks LevelHooks) Add(hook Hook) {
for _, level := range hook.Levels() {
hooks[level] = append(hooks[level], hook)
}
}
// Fire all the hooks for the passed level. Used by `entry.log` to fire
// appropriate hooks for a log entry.
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
for _, hook := range hooks[level] {
if err := hook.Fire(entry); err != nil {
return err
}
}
return nil
}

View file

@ -0,0 +1,39 @@
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
## Usage
```go
import (
"log/syslog"
"github.com/sirupsen/logrus"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
)
func main() {
log := logrus.New()
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err == nil {
log.Hooks.Add(hook)
}
}
```
If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
```go
import (
"log/syslog"
"github.com/sirupsen/logrus"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
)
func main() {
log := logrus.New()
hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
if err == nil {
log.Hooks.Add(hook)
}
}
```

View file

@ -0,0 +1,54 @@
// +build !windows,!nacl,!plan9
package logrus_syslog
import (
"fmt"
"github.com/sirupsen/logrus"
"log/syslog"
"os"
)
// SyslogHook to send logs via syslog.
type SyslogHook struct {
Writer *syslog.Writer
SyslogNetwork string
SyslogRaddr string
}
// Creates a hook to be added to an instance of logger. This is called with
// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
// `if err == nil { log.Hooks.Add(hook) }`
func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
w, err := syslog.Dial(network, raddr, priority, tag)
return &SyslogHook{w, network, raddr}, err
}
func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
line, err := entry.String()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
return err
}
switch entry.Level {
case logrus.PanicLevel:
return hook.Writer.Crit(line)
case logrus.FatalLevel:
return hook.Writer.Crit(line)
case logrus.ErrorLevel:
return hook.Writer.Err(line)
case logrus.WarnLevel:
return hook.Writer.Warning(line)
case logrus.InfoLevel:
return hook.Writer.Info(line)
case logrus.DebugLevel:
return hook.Writer.Debug(line)
default:
return nil
}
}
func (hook *SyslogHook) Levels() []logrus.Level {
return logrus.AllLevels
}

View file

@ -0,0 +1,26 @@
package logrus_syslog
import (
"github.com/sirupsen/logrus"
"log/syslog"
"testing"
)
func TestLocalhostAddAndPrint(t *testing.T) {
log := logrus.New()
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
if err != nil {
t.Errorf("Unable to connect to local syslog.")
}
log.Hooks.Add(hook)
for _, level := range hook.Levels() {
if len(log.Hooks[level]) != 1 {
t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
}
}
log.Info("Congratulations!")
}

95
vendor/github.com/Sirupsen/logrus/hooks/test/test.go generated vendored Normal file
View file

@ -0,0 +1,95 @@
// The Test package is used for testing logrus. It is here for backwards
// compatibility from when logrus' organization was upper-case. Please use
// lower-case logrus and the `null` package instead of this one.
package test
import (
"io/ioutil"
"sync"
"github.com/sirupsen/logrus"
)
// Hook is a hook designed for dealing with logs in test scenarios.
type Hook struct {
// Entries is an array of all entries that have been received by this hook.
// For safe access, use the AllEntries() method, rather than reading this
// value directly.
Entries []*logrus.Entry
mu sync.RWMutex
}
// NewGlobal installs a test hook for the global logger.
func NewGlobal() *Hook {
hook := new(Hook)
logrus.AddHook(hook)
return hook
}
// NewLocal installs a test hook for a given local logger.
func NewLocal(logger *logrus.Logger) *Hook {
hook := new(Hook)
logger.Hooks.Add(hook)
return hook
}
// NewNullLogger creates a discarding logger and installs the test hook.
func NewNullLogger() (*logrus.Logger, *Hook) {
logger := logrus.New()
logger.Out = ioutil.Discard
return logger, NewLocal(logger)
}
func (t *Hook) Fire(e *logrus.Entry) error {
t.mu.Lock()
defer t.mu.Unlock()
t.Entries = append(t.Entries, e)
return nil
}
func (t *Hook) Levels() []logrus.Level {
return logrus.AllLevels
}
// LastEntry returns the last entry that was logged or nil.
func (t *Hook) LastEntry() *logrus.Entry {
t.mu.RLock()
defer t.mu.RUnlock()
i := len(t.Entries) - 1
if i < 0 {
return nil
}
// Make a copy, for safety
e := *t.Entries[i]
return &e
}
// AllEntries returns all entries that were logged.
func (t *Hook) AllEntries() []*logrus.Entry {
t.mu.RLock()
defer t.mu.RUnlock()
// Make a copy so the returned value won't race with future log requests
entries := make([]*logrus.Entry, len(t.Entries))
for i, entry := range t.Entries {
// Make a copy, for safety
e := *entry
entries[i] = &e
}
return entries
}
// Reset removes all Entries from this test hook.
func (t *Hook) Reset() {
t.mu.Lock()
defer t.mu.Unlock()
t.Entries = make([]*logrus.Entry, 0)
}

View file

@ -0,0 +1,39 @@
package test
import (
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
func TestAllHooks(t *testing.T) {
assert := assert.New(t)
logger, hook := NewNullLogger()
assert.Nil(hook.LastEntry())
assert.Equal(0, len(hook.Entries))
logger.Error("Hello error")
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal("Hello error", hook.LastEntry().Message)
assert.Equal(1, len(hook.Entries))
logger.Warn("Hello warning")
assert.Equal(logrus.WarnLevel, hook.LastEntry().Level)
assert.Equal("Hello warning", hook.LastEntry().Message)
assert.Equal(2, len(hook.Entries))
hook.Reset()
assert.Nil(hook.LastEntry())
assert.Equal(0, len(hook.Entries))
hook = NewGlobal()
logrus.Error("Hello error")
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
assert.Equal("Hello error", hook.LastEntry().Message)
assert.Equal(1, len(hook.Entries))
}

74
vendor/github.com/Sirupsen/logrus/json_formatter.go generated vendored Normal file
View file

@ -0,0 +1,74 @@
package logrus
import (
"encoding/json"
"fmt"
)
type fieldKey string
type FieldMap map[fieldKey]string
const (
FieldKeyMsg = "msg"
FieldKeyLevel = "level"
FieldKeyTime = "time"
)
func (f FieldMap) resolve(key fieldKey) string {
if k, ok := f[key]; ok {
return k
}
return string(key)
}
type JSONFormatter struct {
// TimestampFormat sets the format used for marshaling timestamps.
TimestampFormat string
// DisableTimestamp allows disabling automatic timestamps in output
DisableTimestamp bool
// FieldMap allows users to customize the names of keys for various fields.
// As an example:
// formatter := &JSONFormatter{
// FieldMap: FieldMap{
// FieldKeyTime: "@timestamp",
// FieldKeyLevel: "@level",
// FieldKeyMsg: "@message",
// },
// }
FieldMap FieldMap
}
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
data := make(Fields, len(entry.Data)+3)
for k, v := range entry.Data {
switch v := v.(type) {
case error:
// Otherwise errors are ignored by `encoding/json`
// https://github.com/sirupsen/logrus/issues/137
data[k] = v.Error()
default:
data[k] = v
}
}
prefixFieldClashes(data)
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = DefaultTimestampFormat
}
if !f.DisableTimestamp {
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
}
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
serialized, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
}
return append(serialized, '\n'), nil
}

View file

@ -0,0 +1,199 @@
package logrus
import (
"encoding/json"
"errors"
"strings"
"testing"
)
func TestErrorNotLost(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["error"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["omg"] != "wild walrus" {
t.Fatal("Error field not set")
}
}
func TestFieldClashWithTime(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("time", "right now!"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.time"] != "right now!" {
t.Fatal("fields.time not set to original time field")
}
if entry["time"] != "0001-01-01T00:00:00Z" {
t.Fatal("time field not set to current time, was: ", entry["time"])
}
}
func TestFieldClashWithMsg(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("msg", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.msg"] != "something" {
t.Fatal("fields.msg not set to original msg field")
}
}
func TestFieldClashWithLevel(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
entry := make(map[string]interface{})
err = json.Unmarshal(b, &entry)
if err != nil {
t.Fatal("Unable to unmarshal formatted entry: ", err)
}
if entry["fields.level"] != "something" {
t.Fatal("fields.level not set to original level field")
}
}
func TestJSONEntryEndsWithNewline(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
if b[len(b)-1] != '\n' {
t.Fatal("Expected JSON log entry to end with a newline")
}
}
func TestJSONMessageKey(t *testing.T) {
formatter := &JSONFormatter{
FieldMap: FieldMap{
FieldKeyMsg: "message",
},
}
b, err := formatter.Format(&Entry{Message: "oh hai"})
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !(strings.Contains(s, "message") && strings.Contains(s, "oh hai")) {
t.Fatal("Expected JSON to format message key")
}
}
func TestJSONLevelKey(t *testing.T) {
formatter := &JSONFormatter{
FieldMap: FieldMap{
FieldKeyLevel: "somelevel",
},
}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, "somelevel") {
t.Fatal("Expected JSON to format level key")
}
}
func TestJSONTimeKey(t *testing.T) {
formatter := &JSONFormatter{
FieldMap: FieldMap{
FieldKeyTime: "timeywimey",
},
}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, "timeywimey") {
t.Fatal("Expected JSON to format time key")
}
}
func TestJSONDisableTimestamp(t *testing.T) {
formatter := &JSONFormatter{
DisableTimestamp: true,
}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if strings.Contains(s, FieldKeyTime) {
t.Error("Did not prevent timestamp", s)
}
}
func TestJSONEnableTimestamp(t *testing.T) {
formatter := &JSONFormatter{}
b, err := formatter.Format(WithField("level", "something"))
if err != nil {
t.Fatal("Unable to format entry: ", err)
}
s := string(b)
if !strings.Contains(s, FieldKeyTime) {
t.Error("Timestamp not present", s)
}
}

317
vendor/github.com/Sirupsen/logrus/logger.go generated vendored Normal file
View file

@ -0,0 +1,317 @@
package logrus
import (
"io"
"os"
"sync"
"sync/atomic"
)
type Logger struct {
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
// file, or leave it default which is `os.Stderr`. You can also set this to
// something more adventorous, such as logging to Kafka.
Out io.Writer
// Hooks for the logger instance. These allow firing events based on logging
// levels and log entries. For example, to send errors to an error tracking
// service, log to StatsD or dump the core on fatal errors.
Hooks LevelHooks
// All log entries pass through the formatter before logged to Out. The
// included formatters are `TextFormatter` and `JSONFormatter` for which
// TextFormatter is the default. In development (when a TTY is attached) it
// logs with colors, but to a file it wouldn't. You can easily implement your
// own that implements the `Formatter` interface, see the `README` or included
// formatters for examples.
Formatter Formatter
// The logging level the logger should log at. This is typically (and defaults
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
// logged. `logrus.Debug` is useful in
Level Level
// Used to sync writing to the log. Locking is enabled by Default
mu MutexWrap
// Reusable empty entry
entryPool sync.Pool
}
type MutexWrap struct {
lock sync.Mutex
disabled bool
}
func (mw *MutexWrap) Lock() {
if !mw.disabled {
mw.lock.Lock()
}
}
func (mw *MutexWrap) Unlock() {
if !mw.disabled {
mw.lock.Unlock()
}
}
func (mw *MutexWrap) Disable() {
mw.disabled = true
}
// Creates a new logger. Configuration should be set by changing `Formatter`,
// `Out` and `Hooks` directly on the default logger instance. You can also just
// instantiate your own:
//
// var log = &Logger{
// Out: os.Stderr,
// Formatter: new(JSONFormatter),
// Hooks: make(LevelHooks),
// Level: logrus.DebugLevel,
// }
//
// It's recommended to make this a global instance called `log`.
func New() *Logger {
return &Logger{
Out: os.Stderr,
Formatter: new(TextFormatter),
Hooks: make(LevelHooks),
Level: InfoLevel,
}
}
func (logger *Logger) newEntry() *Entry {
entry, ok := logger.entryPool.Get().(*Entry)
if ok {
return entry
}
return NewEntry(logger)
}
func (logger *Logger) releaseEntry(entry *Entry) {
logger.entryPool.Put(entry)
}
// Adds a field to the log entry, note that it doesn't log until you call
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
// If you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithField(key, value)
}
// Adds a struct of fields to the log entry. All it does is call `WithField` for
// each `Field`.
func (logger *Logger) WithFields(fields Fields) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithFields(fields)
}
// Add an error as single field to the log entry. All it does is call
// `WithError` for the given `error`.
func (logger *Logger) WithError(err error) *Entry {
entry := logger.newEntry()
defer logger.releaseEntry(entry)
return entry.WithError(err)
}
func (logger *Logger) Debugf(format string, args ...interface{}) {
if logger.level() >= DebugLevel {
entry := logger.newEntry()
entry.Debugf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Infof(format string, args ...interface{}) {
if logger.level() >= InfoLevel {
entry := logger.newEntry()
entry.Infof(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Printf(format string, args ...interface{}) {
entry := logger.newEntry()
entry.Printf(format, args...)
logger.releaseEntry(entry)
}
func (logger *Logger) Warnf(format string, args ...interface{}) {
if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Warningf(format string, args ...interface{}) {
if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Errorf(format string, args ...interface{}) {
if logger.level() >= ErrorLevel {
entry := logger.newEntry()
entry.Errorf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Fatalf(format string, args ...interface{}) {
if logger.level() >= FatalLevel {
entry := logger.newEntry()
entry.Fatalf(format, args...)
logger.releaseEntry(entry)
}
Exit(1)
}
func (logger *Logger) Panicf(format string, args ...interface{}) {
if logger.level() >= PanicLevel {
entry := logger.newEntry()
entry.Panicf(format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Debug(args ...interface{}) {
if logger.level() >= DebugLevel {
entry := logger.newEntry()
entry.Debug(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Info(args ...interface{}) {
if logger.level() >= InfoLevel {
entry := logger.newEntry()
entry.Info(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Print(args ...interface{}) {
entry := logger.newEntry()
entry.Info(args...)
logger.releaseEntry(entry)
}
func (logger *Logger) Warn(args ...interface{}) {
if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Warning(args ...interface{}) {
if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Error(args ...interface{}) {
if logger.level() >= ErrorLevel {
entry := logger.newEntry()
entry.Error(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Fatal(args ...interface{}) {
if logger.level() >= FatalLevel {
entry := logger.newEntry()
entry.Fatal(args...)
logger.releaseEntry(entry)
}
Exit(1)
}
func (logger *Logger) Panic(args ...interface{}) {
if logger.level() >= PanicLevel {
entry := logger.newEntry()
entry.Panic(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Debugln(args ...interface{}) {
if logger.level() >= DebugLevel {
entry := logger.newEntry()
entry.Debugln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Infoln(args ...interface{}) {
if logger.level() >= InfoLevel {
entry := logger.newEntry()
entry.Infoln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Println(args ...interface{}) {
entry := logger.newEntry()
entry.Println(args...)
logger.releaseEntry(entry)
}
func (logger *Logger) Warnln(args ...interface{}) {
if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Warningln(args ...interface{}) {
if logger.level() >= WarnLevel {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Errorln(args ...interface{}) {
if logger.level() >= ErrorLevel {
entry := logger.newEntry()
entry.Errorln(args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Fatalln(args ...interface{}) {
if logger.level() >= FatalLevel {
entry := logger.newEntry()
entry.Fatalln(args...)
logger.releaseEntry(entry)
}
Exit(1)
}
func (logger *Logger) Panicln(args ...interface{}) {
if logger.level() >= PanicLevel {
entry := logger.newEntry()
entry.Panicln(args...)
logger.releaseEntry(entry)
}
}
//When file is opened with appending mode, it's safe to
//write concurrently to a file (within 4k message on Linux).
//In these cases user can choose to disable the lock.
func (logger *Logger) SetNoLock() {
logger.mu.Disable()
}
func (logger *Logger) level() Level {
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
}
func (logger *Logger) setLevel(level Level) {
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
}

61
vendor/github.com/Sirupsen/logrus/logger_bench_test.go generated vendored Normal file
View file

@ -0,0 +1,61 @@
package logrus
import (
"os"
"testing"
)
// smallFields is a small size data set for benchmarking
var loggerFields = Fields{
"foo": "bar",
"baz": "qux",
"one": "two",
"three": "four",
}
func BenchmarkDummyLogger(b *testing.B) {
nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666)
if err != nil {
b.Fatalf("%v", err)
}
defer nullf.Close()
doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
}
func BenchmarkDummyLoggerNoLock(b *testing.B) {
nullf, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
b.Fatalf("%v", err)
}
defer nullf.Close()
doLoggerBenchmarkNoLock(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
}
func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
logger := Logger{
Out: out,
Level: InfoLevel,
Formatter: formatter,
}
entry := logger.WithFields(fields)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entry.Info("aaa")
}
})
}
func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
logger := Logger{
Out: out,
Level: InfoLevel,
Formatter: formatter,
}
logger.SetNoLock()
entry := logger.WithFields(fields)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
entry.Info("aaa")
}
})
}

143
vendor/github.com/Sirupsen/logrus/logrus.go generated vendored Normal file
View file

@ -0,0 +1,143 @@
package logrus
import (
"fmt"
"log"
"strings"
)
// Fields type, used to pass to `WithFields`.
type Fields map[string]interface{}
// Level type
type Level uint32
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
func (level Level) String() string {
switch level {
case DebugLevel:
return "debug"
case InfoLevel:
return "info"
case WarnLevel:
return "warning"
case ErrorLevel:
return "error"
case FatalLevel:
return "fatal"
case PanicLevel:
return "panic"
}
return "unknown"
}
// ParseLevel takes a string level and returns the Logrus log level constant.
func ParseLevel(lvl string) (Level, error) {
switch strings.ToLower(lvl) {
case "panic":
return PanicLevel, nil
case "fatal":
return FatalLevel, nil
case "error":
return ErrorLevel, nil
case "warn", "warning":
return WarnLevel, nil
case "info":
return InfoLevel, nil
case "debug":
return DebugLevel, nil
}
var l Level
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
}
// A constant exposing all logging levels
var AllLevels = []Level{
PanicLevel,
FatalLevel,
ErrorLevel,
WarnLevel,
InfoLevel,
DebugLevel,
}
// These are the different logging levels. You can set the logging level to log
// on your instance of logger, obtained with `logrus.New()`.
const (
// PanicLevel level, highest level of severity. Logs and then calls panic with the
// message passed to Debug, Info, ...
PanicLevel Level = iota
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
// logging level is set to Panic.
FatalLevel
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
// Commonly used for hooks to send errors to an error tracking service.
ErrorLevel
// WarnLevel level. Non-critical entries that deserve eyes.
WarnLevel
// InfoLevel level. General operational entries about what's going on inside the
// application.
InfoLevel
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
DebugLevel
)
// Won't compile if StdLogger can't be realized by a log.Logger
var (
_ StdLogger = &log.Logger{}
_ StdLogger = &Entry{}
_ StdLogger = &Logger{}
)
// StdLogger is what your logrus-enabled library should take, that way
// it'll accept a stdlib logger and a logrus logger. There's no standard
// interface, this is the closest we get, unfortunately.
type StdLogger interface {
Print(...interface{})
Printf(string, ...interface{})
Println(...interface{})
Fatal(...interface{})
Fatalf(string, ...interface{})
Fatalln(...interface{})
Panic(...interface{})
Panicf(string, ...interface{})
Panicln(...interface{})
}
// The FieldLogger interface generalizes the Entry and Logger types
type FieldLogger interface {
WithField(key string, value interface{}) *Entry
WithFields(fields Fields) *Entry
WithError(err error) *Entry
Debugf(format string, args ...interface{})
Infof(format string, args ...interface{})
Printf(format string, args ...interface{})
Warnf(format string, args ...interface{})
Warningf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
Panicf(format string, args ...interface{})
Debug(args ...interface{})
Info(args ...interface{})
Print(args ...interface{})
Warn(args ...interface{})
Warning(args ...interface{})
Error(args ...interface{})
Fatal(args ...interface{})
Panic(args ...interface{})
Debugln(args ...interface{})
Infoln(args ...interface{})
Println(args ...interface{})
Warnln(args ...interface{})
Warningln(args ...interface{})
Errorln(args ...interface{})
Fatalln(args ...interface{})
Panicln(args ...interface{})
}

386
vendor/github.com/Sirupsen/logrus/logrus_test.go generated vendored Normal file
View file

@ -0,0 +1,386 @@
package logrus
import (
"bytes"
"encoding/json"
"strconv"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
log(logger)
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assertions(fields)
}
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
var buffer bytes.Buffer
logger := New()
logger.Out = &buffer
logger.Formatter = &TextFormatter{
DisableColors: true,
}
log(logger)
fields := make(map[string]string)
for _, kv := range strings.Split(buffer.String(), " ") {
if !strings.Contains(kv, "=") {
continue
}
kvArr := strings.Split(kv, "=")
key := strings.TrimSpace(kvArr[0])
val := kvArr[1]
if kvArr[1][0] == '"' {
var err error
val, err = strconv.Unquote(val)
assert.NoError(t, err)
}
fields[key] = val
}
assertions(fields)
}
func TestPrint(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Print("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestInfo(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "info")
})
}
func TestWarn(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Warn("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["level"], "warning")
})
}
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test test")
})
}
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test 10")
})
}
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Infoln(10, 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "10 10")
})
}
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", 10)
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test10")
})
}
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.Info("test", "test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "testtest")
})
}
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
localLog := logger.WithFields(Fields{
"key1": "value1",
})
localLog.WithField("key2", "value2").Info("test")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
assert.Equal(t, "value2", fields["key2"])
assert.Equal(t, "value1", fields["key1"])
buffer = bytes.Buffer{}
fields = Fields{}
localLog.Info("test")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.Nil(t, err)
_, ok := fields["key2"]
assert.Equal(t, false, ok)
assert.Equal(t, "value1", fields["key1"])
}
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
})
}
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("msg", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["msg"], "test")
assert.Equal(t, fields["fields.msg"], "hello")
})
}
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("time", "hello").Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["fields.time"], "hello")
})
}
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
LogAndAssertJSON(t, func(log *Logger) {
log.WithField("level", 1).Info("test")
}, func(fields Fields) {
assert.Equal(t, fields["level"], "info")
assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
})
}
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
LogAndAssertText(t, func(log *Logger) {
ll := log.WithField("herp", "derp")
ll.Info("hello")
ll.Info("bye")
}, func(fields map[string]string) {
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
if _, ok := fields[fieldName]; ok {
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
}
}
})
}
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
var buffer bytes.Buffer
var fields Fields
logger := New()
logger.Out = &buffer
logger.Formatter = new(JSONFormatter)
llog := logger.WithField("context", "eating raw fish")
llog.Info("looks delicious")
err := json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded first message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "looks delicious")
assert.Equal(t, fields["context"], "eating raw fish")
buffer.Reset()
llog.Warn("omg it is!")
err = json.Unmarshal(buffer.Bytes(), &fields)
assert.NoError(t, err, "should have decoded second message")
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
assert.Equal(t, fields["msg"], "omg it is!")
assert.Equal(t, fields["context"], "eating raw fish")
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
}
func TestConvertLevelToString(t *testing.T) {
assert.Equal(t, "debug", DebugLevel.String())
assert.Equal(t, "info", InfoLevel.String())
assert.Equal(t, "warning", WarnLevel.String())
assert.Equal(t, "error", ErrorLevel.String())
assert.Equal(t, "fatal", FatalLevel.String())
assert.Equal(t, "panic", PanicLevel.String())
}
func TestParseLevel(t *testing.T) {
l, err := ParseLevel("panic")
assert.Nil(t, err)
assert.Equal(t, PanicLevel, l)
l, err = ParseLevel("PANIC")
assert.Nil(t, err)
assert.Equal(t, PanicLevel, l)
l, err = ParseLevel("fatal")
assert.Nil(t, err)
assert.Equal(t, FatalLevel, l)
l, err = ParseLevel("FATAL")
assert.Nil(t, err)
assert.Equal(t, FatalLevel, l)
l, err = ParseLevel("error")
assert.Nil(t, err)
assert.Equal(t, ErrorLevel, l)
l, err = ParseLevel("ERROR")
assert.Nil(t, err)
assert.Equal(t, ErrorLevel, l)
l, err = ParseLevel("warn")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("WARN")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("warning")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("WARNING")
assert.Nil(t, err)
assert.Equal(t, WarnLevel, l)
l, err = ParseLevel("info")
assert.Nil(t, err)
assert.Equal(t, InfoLevel, l)
l, err = ParseLevel("INFO")
assert.Nil(t, err)
assert.Equal(t, InfoLevel, l)
l, err = ParseLevel("debug")
assert.Nil(t, err)
assert.Equal(t, DebugLevel, l)
l, err = ParseLevel("DEBUG")
assert.Nil(t, err)
assert.Equal(t, DebugLevel, l)
l, err = ParseLevel("invalid")
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
}
func TestGetSetLevelRace(t *testing.T) {
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if i%2 == 0 {
SetLevel(InfoLevel)
} else {
GetLevel()
}
}(i)
}
wg.Wait()
}
func TestLoggingRace(t *testing.T) {
logger := New()
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
logger.Info("info")
wg.Done()
}()
}
wg.Wait()
}
// Compile test
func TestLogrusInterface(t *testing.T) {
var buffer bytes.Buffer
fn := func(l FieldLogger) {
b := l.WithField("key", "value")
b.Debug("Test")
}
// test logger
logger := New()
logger.Out = &buffer
fn(logger)
// test Entry
e := logger.WithField("another", "value")
fn(e)
}
// Implements io.Writer using channels for synchronization, so we can wait on
// the Entry.Writer goroutine to write in a non-racey way. This does assume that
// there is a single call to Logger.Out for each message.
type channelWriter chan []byte
func (cw channelWriter) Write(p []byte) (int, error) {
cw <- p
return len(p), nil
}
func TestEntryWriter(t *testing.T) {
cw := channelWriter(make(chan []byte, 1))
log := New()
log.Out = cw
log.Formatter = new(JSONFormatter)
log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n"))
bs := <-cw
var fields Fields
err := json.Unmarshal(bs, &fields)
assert.Nil(t, err)
assert.Equal(t, fields["foo"], "bar")
assert.Equal(t, fields["level"], "warning")
}

View file

@ -0,0 +1,10 @@
// +build appengine
package logrus
import "io"
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal(f io.Writer) bool {
return true
}

10
vendor/github.com/Sirupsen/logrus/terminal_bsd.go generated vendored Normal file
View file

@ -0,0 +1,10 @@
// +build darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package logrus
import "syscall"
const ioctlReadTermios = syscall.TIOCGETA
type Termios syscall.Termios

14
vendor/github.com/Sirupsen/logrus/terminal_linux.go generated vendored Normal file
View file

@ -0,0 +1,14 @@
// Based on ssh/terminal:
// Copyright 2013 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.
// +build !appengine
package logrus
import "syscall"
const ioctlReadTermios = syscall.TCGETS
type Termios syscall.Termios

View file

@ -0,0 +1,28 @@
// Based on ssh/terminal:
// Copyright 2011 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.
// +build linux darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package logrus
import (
"io"
"os"
"syscall"
"unsafe"
)
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal(f io.Writer) bool {
var termios Termios
switch v := f.(type) {
case *os.File:
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
default:
return false
}
}

21
vendor/github.com/Sirupsen/logrus/terminal_solaris.go generated vendored Normal file
View file

@ -0,0 +1,21 @@
// +build solaris,!appengine
package logrus
import (
"io"
"os"
"golang.org/x/sys/unix"
)
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(f io.Writer) bool {
switch v := f.(type) {
case *os.File:
_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
return err == nil
default:
return false
}
}

82
vendor/github.com/Sirupsen/logrus/terminal_windows.go generated vendored Normal file
View file

@ -0,0 +1,82 @@
// Based on ssh/terminal:
// Copyright 2011 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.
// +build windows,!appengine
package logrus
import (
"bytes"
"errors"
"io"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"unsafe"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var (
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
)
const (
enableProcessedOutput = 0x0001
enableWrapAtEolOutput = 0x0002
enableVirtualTerminalProcessing = 0x0004
)
func getVersion() (float64, error) {
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
cmd := exec.Command("cmd", "ver")
cmd.Stdout = stdout
cmd.Stderr = stderr
err := cmd.Run()
if err != nil {
return -1, err
}
// The output should be like "Microsoft Windows [Version XX.X.XXXXXX]"
version := strings.Replace(stdout.String(), "\n", "", -1)
version = strings.Replace(version, "\r\n", "", -1)
x1 := strings.Index(version, "[Version")
if x1 == -1 || strings.Index(version, "]") == -1 {
return -1, errors.New("Can't determine Windows version")
}
return strconv.ParseFloat(version[x1+9:x1+13], 64)
}
func init() {
ver, err := getVersion()
if err != nil {
return
}
// Activate Virtual Processing for Windows CMD
// Info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
if ver >= 10 {
handle := syscall.Handle(os.Stderr.Fd())
procSetConsoleMode.Call(uintptr(handle), enableProcessedOutput|enableWrapAtEolOutput|enableVirtualTerminalProcessing)
}
}
// IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal(f io.Writer) bool {
switch v := f.(type) {
case *os.File:
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
default:
return false
}
}

189
vendor/github.com/Sirupsen/logrus/text_formatter.go generated vendored Normal file
View file

@ -0,0 +1,189 @@
package logrus
import (
"bytes"
"fmt"
"sort"
"strings"
"sync"
"time"
)
const (
nocolor = 0
red = 31
green = 32
yellow = 33
blue = 34
gray = 37
)
var (
baseTimestamp time.Time
)
func init() {
baseTimestamp = time.Now()
}
type TextFormatter struct {
// Set to true to bypass checking for a TTY before outputting colors.
ForceColors bool
// Force disabling colors.
DisableColors bool
// Disable timestamp logging. useful when output is redirected to logging
// system that already adds timestamps.
DisableTimestamp bool
// Enable logging the full timestamp when a TTY is attached instead of just
// the time passed since beginning of execution.
FullTimestamp bool
// TimestampFormat to use for display when a full timestamp is printed
TimestampFormat string
// The fields are sorted by default for a consistent output. For applications
// that log extremely frequently and don't use the JSON formatter this may not
// be desired.
DisableSorting bool
// QuoteEmptyFields will wrap empty fields in quotes if true
QuoteEmptyFields bool
// QuoteCharacter can be set to the override the default quoting character "
// with something else. For example: ', or `.
QuoteCharacter string
// Whether the logger's out is to a terminal
isTerminal bool
sync.Once
}
func (f *TextFormatter) init(entry *Entry) {
if len(f.QuoteCharacter) == 0 {
f.QuoteCharacter = "\""
}
if entry.Logger != nil {
f.isTerminal = IsTerminal(entry.Logger.Out)
}
}
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
var b *bytes.Buffer
keys := make([]string, 0, len(entry.Data))
for k := range entry.Data {
keys = append(keys, k)
}
if !f.DisableSorting {
sort.Strings(keys)
}
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
prefixFieldClashes(entry.Data)
f.Do(func() { f.init(entry) })
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
timestampFormat := f.TimestampFormat
if timestampFormat == "" {
timestampFormat = DefaultTimestampFormat
}
if isColored {
f.printColored(b, entry, keys, timestampFormat)
} else {
if !f.DisableTimestamp {
f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
}
f.appendKeyValue(b, "level", entry.Level.String())
if entry.Message != "" {
f.appendKeyValue(b, "msg", entry.Message)
}
for _, key := range keys {
f.appendKeyValue(b, key, entry.Data[key])
}
}
b.WriteByte('\n')
return b.Bytes(), nil
}
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
var levelColor int
switch entry.Level {
case DebugLevel:
levelColor = gray
case WarnLevel:
levelColor = yellow
case ErrorLevel, FatalLevel, PanicLevel:
levelColor = red
default:
levelColor = blue
}
levelText := strings.ToUpper(entry.Level.String())[0:4]
if f.DisableTimestamp {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
} else if !f.FullTimestamp {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
} else {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
}
for _, k := range keys {
v := entry.Data[k]
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
f.appendValue(b, v)
}
}
func (f *TextFormatter) needsQuoting(text string) bool {
if f.QuoteEmptyFields && len(text) == 0 {
return true
}
for _, ch := range text {
if !((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
ch == '-' || ch == '.') {
return true
}
}
return false
}
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
b.WriteString(key)
b.WriteByte('=')
f.appendValue(b, value)
b.WriteByte(' ')
}
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
switch value := value.(type) {
case string:
if !f.needsQuoting(value) {
b.WriteString(value)
} else {
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
}
case error:
errmsg := value.Error()
if !f.needsQuoting(errmsg) {
b.WriteString(errmsg)
} else {
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
}
default:
fmt.Fprint(b, value)
}
}

View file

@ -0,0 +1,87 @@
package logrus
import (
"bytes"
"errors"
"strings"
"testing"
"time"
)
func TestQuoting(t *testing.T) {
tf := &TextFormatter{DisableColors: true}
checkQuoting := func(q bool, value interface{}) {
b, _ := tf.Format(WithField("test", value))
idx := bytes.Index(b, ([]byte)("test="))
cont := bytes.Contains(b[idx+5:], []byte(tf.QuoteCharacter))
if cont != q {
if q {
t.Errorf("quoting expected for: %#v", value)
} else {
t.Errorf("quoting not expected for: %#v", value)
}
}
}
checkQuoting(false, "")
checkQuoting(false, "abcd")
checkQuoting(false, "v1.0")
checkQuoting(false, "1234567890")
checkQuoting(true, "/foobar")
checkQuoting(true, "x y")
checkQuoting(true, "x,y")
checkQuoting(false, errors.New("invalid"))
checkQuoting(true, errors.New("invalid argument"))
// Test for custom quote character.
tf.QuoteCharacter = "`"
checkQuoting(false, "")
checkQuoting(false, "abcd")
checkQuoting(true, "/foobar")
checkQuoting(true, errors.New("invalid argument"))
// Test for multi-character quotes.
tf.QuoteCharacter = "§~±"
checkQuoting(false, "abcd")
checkQuoting(true, errors.New("invalid argument"))
// Test for quoting empty fields.
tf.QuoteEmptyFields = true
checkQuoting(true, "")
checkQuoting(false, "abcd")
checkQuoting(true, errors.New("invalid argument"))
}
func TestTimestampFormat(t *testing.T) {
checkTimeStr := func(format string) {
customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
customStr, _ := customFormatter.Format(WithField("test", "test"))
timeStart := bytes.Index(customStr, ([]byte)("time="))
timeEnd := bytes.Index(customStr, ([]byte)("level="))
timeStr := customStr[timeStart+5+len(customFormatter.QuoteCharacter) : timeEnd-1-len(customFormatter.QuoteCharacter)]
if format == "" {
format = time.RFC3339
}
_, e := time.Parse(format, (string)(timeStr))
if e != nil {
t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
}
}
checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
checkTimeStr("Mon Jan _2 15:04:05 2006")
checkTimeStr("")
}
func TestDisableTimestampWithColoredOutput(t *testing.T) {
tf := &TextFormatter{DisableTimestamp: true, ForceColors: true}
b, _ := tf.Format(WithField("test", "test"))
if strings.Contains(string(b), "[0000]") {
t.Error("timestamp not expected when DisableTimestamp is true")
}
}
// TODO add tests for sorting etc., this requires a parser for the text
// formatter output.

62
vendor/github.com/Sirupsen/logrus/writer.go generated vendored Normal file
View file

@ -0,0 +1,62 @@
package logrus
import (
"bufio"
"io"
"runtime"
)
func (logger *Logger) Writer() *io.PipeWriter {
return logger.WriterLevel(InfoLevel)
}
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
return NewEntry(logger).WriterLevel(level)
}
func (entry *Entry) Writer() *io.PipeWriter {
return entry.WriterLevel(InfoLevel)
}
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
reader, writer := io.Pipe()
var printFunc func(args ...interface{})
switch level {
case DebugLevel:
printFunc = entry.Debug
case InfoLevel:
printFunc = entry.Info
case WarnLevel:
printFunc = entry.Warn
case ErrorLevel:
printFunc = entry.Error
case FatalLevel:
printFunc = entry.Fatal
case PanicLevel:
printFunc = entry.Panic
default:
printFunc = entry.Print
}
go entry.writerScanner(reader, printFunc)
runtime.SetFinalizer(writer, writerFinalizer)
return writer
}
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
printFunc(scanner.Text())
}
if err := scanner.Err(); err != nil {
entry.Errorf("Error while reading from Writer: %s", err)
}
reader.Close()
}
func writerFinalizer(writer *io.PipeWriter) {
writer.Close()
}

View file

@ -15,6 +15,7 @@ package acme
import ( import (
"bytes" "bytes"
"context"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
@ -36,9 +37,6 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
) )
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA. // LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
@ -47,6 +45,10 @@ const LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
const ( const (
maxChainLen = 5 // max depth and breadth of a certificate chain maxChainLen = 5 // max depth and breadth of a certificate chain
maxCertSize = 1 << 20 // max size of a certificate, in bytes maxCertSize = 1 << 20 // max size of a certificate, in bytes
// Max number of collected nonces kept in memory.
// Expect usual peak of 1 or 2.
maxNonces = 100
) )
// CertOption is an optional argument type for Client methods which manipulate // CertOption is an optional argument type for Client methods which manipulate
@ -108,6 +110,9 @@ type Client struct {
dirMu sync.Mutex // guards writes to dir dirMu sync.Mutex // guards writes to dir
dir *Directory // cached result of Client's Discover method dir *Directory // cached result of Client's Discover method
noncesMu sync.Mutex
nonces map[string]struct{} // nonces collected from previous responses
} }
// Discover performs ACME server discovery using c.DirectoryURL. // Discover performs ACME server discovery using c.DirectoryURL.
@ -126,11 +131,12 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
if dirURL == "" { if dirURL == "" {
dirURL = LetsEncryptURL dirURL = LetsEncryptURL
} }
res, err := ctxhttp.Get(ctx, c.HTTPClient, dirURL) res, err := c.get(ctx, dirURL)
if err != nil { if err != nil {
return Directory{}, err return Directory{}, err
} }
defer res.Body.Close() defer res.Body.Close()
c.addNonce(res.Header)
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
return Directory{}, responseError(res) return Directory{}, responseError(res)
} }
@ -146,7 +152,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
CAA []string `json:"caa-identities"` CAA []string `json:"caa-identities"`
} }
} }
if json.NewDecoder(res.Body).Decode(&v); err != nil { if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
return Directory{}, err return Directory{}, err
} }
c.dir = &Directory{ c.dir = &Directory{
@ -192,7 +198,7 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
req.NotAfter = now.Add(exp).Format(time.RFC3339) req.NotAfter = now.Add(exp).Format(time.RFC3339)
} }
res, err := postJWS(ctx, c.HTTPClient, c.Key, c.dir.CertURL, req) res, err := c.retryPostJWS(ctx, c.Key, c.dir.CertURL, req)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
@ -208,7 +214,7 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
return cert, curl, err return cert, curl, err
} }
// slurp issued cert and CA chain, if requested // slurp issued cert and CA chain, if requested
cert, err := responseCert(ctx, c.HTTPClient, res, bundle) cert, err := c.responseCert(ctx, res, bundle)
return cert, curl, err return cert, curl, err
} }
@ -223,13 +229,13 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
// and has expected features. // and has expected features.
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) { func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
for { for {
res, err := ctxhttp.Get(ctx, c.HTTPClient, url) res, err := c.get(ctx, url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer res.Body.Close() defer res.Body.Close()
if res.StatusCode == http.StatusOK { if res.StatusCode == http.StatusOK {
return responseCert(ctx, c.HTTPClient, res, bundle) return c.responseCert(ctx, res, bundle)
} }
if res.StatusCode > 299 { if res.StatusCode > 299 {
return nil, responseError(res) return nil, responseError(res)
@ -267,7 +273,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
if key == nil { if key == nil {
key = c.Key key = c.Key
} }
res, err := postJWS(ctx, c.HTTPClient, key, c.dir.RevokeURL, body) res, err := c.retryPostJWS(ctx, key, c.dir.RevokeURL, body)
if err != nil { if err != nil {
return err return err
} }
@ -355,7 +361,7 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
Resource: "new-authz", Resource: "new-authz",
Identifier: authzID{Type: "dns", Value: domain}, Identifier: authzID{Type: "dns", Value: domain},
} }
res, err := postJWS(ctx, c.HTTPClient, c.Key, c.dir.AuthzURL, req) res, err := c.retryPostJWS(ctx, c.Key, c.dir.AuthzURL, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -379,7 +385,7 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
// If a caller needs to poll an authorization until its status is final, // If a caller needs to poll an authorization until its status is final,
// see the WaitAuthorization method. // see the WaitAuthorization method.
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) { func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
res, err := ctxhttp.Get(ctx, c.HTTPClient, url) res, err := c.get(ctx, url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -413,7 +419,7 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
Status: "deactivated", Status: "deactivated",
Delete: true, Delete: true,
} }
res, err := postJWS(ctx, c.HTTPClient, c.Key, url, req) res, err := c.retryPostJWS(ctx, c.Key, url, req)
if err != nil { if err != nil {
return err return err
} }
@ -430,25 +436,11 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
// //
// It returns a non-nil Authorization only if its Status is StatusValid. // It returns a non-nil Authorization only if its Status is StatusValid.
// In all other cases WaitAuthorization returns an error. // In all other cases WaitAuthorization returns an error.
// If the Status is StatusInvalid, the returned error is ErrAuthorizationFailed. // If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) { func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
var count int sleep := sleeper(ctx)
sleep := func(v string, inc int) error {
count += inc
d := backoff(count, 10*time.Second)
d = retryAfter(v, d)
wakeup := time.NewTimer(d)
defer wakeup.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-wakeup.C:
return nil
}
}
for { for {
res, err := ctxhttp.Get(ctx, c.HTTPClient, url) res, err := c.get(ctx, url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -473,7 +465,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
return raw.authorization(url), nil return raw.authorization(url), nil
} }
if raw.Status == StatusInvalid { if raw.Status == StatusInvalid {
return nil, ErrAuthorizationFailed return nil, raw.error(url)
} }
if err := sleep(retry, 0); err != nil { if err := sleep(retry, 0); err != nil {
return nil, err return nil, err
@ -485,7 +477,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
// //
// A client typically polls a challenge status using this method. // A client typically polls a challenge status using this method.
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) { func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
res, err := ctxhttp.Get(ctx, c.HTTPClient, url) res, err := c.get(ctx, url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -519,7 +511,7 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error
Type: chal.Type, Type: chal.Type,
Auth: auth, Auth: auth,
} }
res, err := postJWS(ctx, c.HTTPClient, c.Key, chal.URI, req) res, err := c.retryPostJWS(ctx, c.Key, chal.URI, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -652,7 +644,7 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
req.Contact = acct.Contact req.Contact = acct.Contact
req.Agreement = acct.AgreedTerms req.Agreement = acct.AgreedTerms
} }
res, err := postJWS(ctx, c.HTTPClient, c.Key, url, req) res, err := c.retryPostJWS(ctx, c.Key, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -689,7 +681,170 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
}, nil }, nil
} }
func responseCert(ctx context.Context, client *http.Client, res *http.Response, bundle bool) ([][]byte, error) { // retryPostJWS will retry calls to postJWS if there is a badNonce error,
// clearing the stored nonces after each error.
// If the response was 4XX-5XX, then responseError is called on the body,
// the body is closed, and the error returned.
func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
sleep := sleeper(ctx)
for {
res, err := c.postJWS(ctx, key, url, body)
if err != nil {
return nil, err
}
// handle errors 4XX-5XX with responseError
if res.StatusCode >= 400 && res.StatusCode <= 599 {
err := responseError(res)
res.Body.Close()
// according to spec badNonce is urn:ietf:params:acme:error:badNonce
// however, acme servers in the wild return their version of the error
// https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
if ae, ok := err.(*Error); ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce") {
// clear any nonces that we might've stored that might now be
// considered bad
c.clearNonces()
retry := res.Header.Get("retry-after")
if err := sleep(retry, 1); err != nil {
return nil, err
}
continue
}
return nil, err
}
return res, nil
}
}
// postJWS signs the body with the given key and POSTs it to the provided url.
// The body argument must be JSON-serializable.
func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
nonce, err := c.popNonce(ctx, url)
if err != nil {
return nil, err
}
b, err := jwsEncodeJSON(body, key, nonce)
if err != nil {
return nil, err
}
res, err := c.post(ctx, url, "application/jose+json", bytes.NewReader(b))
if err != nil {
return nil, err
}
c.addNonce(res.Header)
return res, nil
}
// popNonce returns a nonce value previously stored with c.addNonce
// or fetches a fresh one from the given URL.
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
c.noncesMu.Lock()
defer c.noncesMu.Unlock()
if len(c.nonces) == 0 {
return c.fetchNonce(ctx, url)
}
var nonce string
for nonce = range c.nonces {
delete(c.nonces, nonce)
break
}
return nonce, nil
}
// clearNonces clears any stored nonces
func (c *Client) clearNonces() {
c.noncesMu.Lock()
defer c.noncesMu.Unlock()
c.nonces = make(map[string]struct{})
}
// addNonce stores a nonce value found in h (if any) for future use.
func (c *Client) addNonce(h http.Header) {
v := nonceFromHeader(h)
if v == "" {
return
}
c.noncesMu.Lock()
defer c.noncesMu.Unlock()
if len(c.nonces) >= maxNonces {
return
}
if c.nonces == nil {
c.nonces = make(map[string]struct{})
}
c.nonces[v] = struct{}{}
}
func (c *Client) httpClient() *http.Client {
if c.HTTPClient != nil {
return c.HTTPClient
}
return http.DefaultClient
}
func (c *Client) get(ctx context.Context, urlStr string) (*http.Response, error) {
req, err := http.NewRequest("GET", urlStr, nil)
if err != nil {
return nil, err
}
return c.do(ctx, req)
}
func (c *Client) head(ctx context.Context, urlStr string) (*http.Response, error) {
req, err := http.NewRequest("HEAD", urlStr, nil)
if err != nil {
return nil, err
}
return c.do(ctx, req)
}
func (c *Client) post(ctx context.Context, urlStr, contentType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", urlStr, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)
return c.do(ctx, req)
}
func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) {
res, err := c.httpClient().Do(req.WithContext(ctx))
if err != nil {
select {
case <-ctx.Done():
// Prefer the unadorned context error.
// (The acme package had tests assuming this, previously from ctxhttp's
// behavior, predating net/http supporting contexts natively)
// TODO(bradfitz): reconsider this in the future. But for now this
// requires no test updates.
return nil, ctx.Err()
default:
return nil, err
}
}
return res, nil
}
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
resp, err := c.head(ctx, url)
if err != nil {
return "", err
}
defer resp.Body.Close()
nonce := nonceFromHeader(resp.Header)
if nonce == "" {
if resp.StatusCode > 299 {
return "", responseError(resp)
}
return "", errors.New("acme: nonce not found")
}
return nonce, nil
}
func nonceFromHeader(h http.Header) string {
return h.Get("Replay-Nonce")
}
func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) {
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1)) b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
if err != nil { if err != nil {
return nil, fmt.Errorf("acme: response stream: %v", err) return nil, fmt.Errorf("acme: response stream: %v", err)
@ -713,7 +868,7 @@ func responseCert(ctx context.Context, client *http.Client, res *http.Response,
return nil, errors.New("acme: rel=up link is too large") return nil, errors.New("acme: rel=up link is too large")
} }
for _, url := range up { for _, url := range up {
cc, err := chainCert(ctx, client, url, 0) cc, err := c.chainCert(ctx, url, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -727,14 +882,8 @@ func responseError(resp *http.Response) error {
// don't care if ReadAll returns an error: // don't care if ReadAll returns an error:
// json.Unmarshal will fail in that case anyway // json.Unmarshal will fail in that case anyway
b, _ := ioutil.ReadAll(resp.Body) b, _ := ioutil.ReadAll(resp.Body)
e := struct { e := &wireError{Status: resp.StatusCode}
Status int if err := json.Unmarshal(b, e); err != nil {
Type string
Detail string
}{
Status: resp.StatusCode,
}
if err := json.Unmarshal(b, &e); err != nil {
// this is not a regular error response: // this is not a regular error response:
// populate detail with anything we received, // populate detail with anything we received,
// e.Status will already contain HTTP response code value // e.Status will already contain HTTP response code value
@ -743,12 +892,7 @@ func responseError(resp *http.Response) error {
e.Detail = resp.Status e.Detail = resp.Status
} }
} }
return &Error{ return e.error(resp.Header)
StatusCode: e.Status,
ProblemType: e.Type,
Detail: e.Detail,
Header: resp.Header,
}
} }
// chainCert fetches CA certificate chain recursively by following "up" links. // chainCert fetches CA certificate chain recursively by following "up" links.
@ -756,12 +900,12 @@ func responseError(resp *http.Response) error {
// if the recursion level reaches maxChainLen. // if the recursion level reaches maxChainLen.
// //
// First chainCert call starts with depth of 0. // First chainCert call starts with depth of 0.
func chainCert(ctx context.Context, client *http.Client, url string, depth int) ([][]byte, error) { func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) {
if depth >= maxChainLen { if depth >= maxChainLen {
return nil, errors.New("acme: certificate chain is too deep") return nil, errors.New("acme: certificate chain is too deep")
} }
res, err := ctxhttp.Get(ctx, client, url) res, err := c.get(ctx, url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -783,7 +927,7 @@ func chainCert(ctx context.Context, client *http.Client, url string, depth int)
return nil, errors.New("acme: certificate chain is too large") return nil, errors.New("acme: certificate chain is too large")
} }
for _, up := range uplink { for _, up := range uplink {
cc, err := chainCert(ctx, client, up, depth+1) cc, err := c.chainCert(ctx, up, depth+1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -793,33 +937,6 @@ func chainCert(ctx context.Context, client *http.Client, url string, depth int)
return chain, nil return chain, nil
} }
// postJWS signs the body with the given key and POSTs it to the provided url.
// The body argument must be JSON-serializable.
func postJWS(ctx context.Context, client *http.Client, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
nonce, err := fetchNonce(ctx, client, url)
if err != nil {
return nil, err
}
b, err := jwsEncodeJSON(body, key, nonce)
if err != nil {
return nil, err
}
return ctxhttp.Post(ctx, client, url, "application/jose+json", bytes.NewReader(b))
}
func fetchNonce(ctx context.Context, client *http.Client, url string) (string, error) {
resp, err := ctxhttp.Head(ctx, client, url)
if err != nil {
return "", nil
}
defer resp.Body.Close()
enc := resp.Header.Get("replay-nonce")
if enc == "" {
return "", errors.New("acme: nonce not found")
}
return enc, nil
}
// linkHeader returns URI-Reference values of all Link headers // linkHeader returns URI-Reference values of all Link headers
// with relation-type rel. // with relation-type rel.
// See https://tools.ietf.org/html/rfc5988#section-5 for details. // See https://tools.ietf.org/html/rfc5988#section-5 for details.
@ -840,6 +957,28 @@ func linkHeader(h http.Header, rel string) []string {
return links return links
} }
// sleeper returns a function that accepts the Retry-After HTTP header value
// and an increment that's used with backoff to increasingly sleep on
// consecutive calls until the context is done. If the Retry-After header
// cannot be parsed, then backoff is used with a maximum sleep time of 10
// seconds.
func sleeper(ctx context.Context) func(ra string, inc int) error {
var count int
return func(ra string, inc int) error {
count += inc
d := backoff(count, 10*time.Second)
d = retryAfter(ra, d)
wakeup := time.NewTimer(d)
defer wakeup.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-wakeup.C:
return nil
}
}
}
// retryAfter parses a Retry-After HTTP header value, // retryAfter parses a Retry-After HTTP header value,
// trying to convert v into an int (seconds) or use http.ParseTime otherwise. // trying to convert v into an int (seconds) or use http.ParseTime otherwise.
// It returns d if v cannot be parsed. // It returns d if v cannot be parsed.
@ -921,7 +1060,8 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
NotBefore: time.Now(), NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour), NotAfter: time.Now().Add(24 * time.Hour),
BasicConstraintsValid: true, BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageKeyEncipherment, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
} }
} }
tmpl.DNSNames = san tmpl.DNSNames = san

View file

@ -6,6 +6,7 @@ package acme
import ( import (
"bytes" "bytes"
"context"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/tls" "crypto/tls"
@ -23,8 +24,6 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"golang.org/x/net/context"
) )
// Decodes a JWS-encoded request and unmarshals the decoded JSON into a provided // Decodes a JWS-encoded request and unmarshals the decoded JSON into a provided
@ -45,6 +44,28 @@ func decodeJWSRequest(t *testing.T, v interface{}, r *http.Request) {
} }
} }
type jwsHead struct {
Alg string
Nonce string
JWK map[string]string `json:"jwk"`
}
func decodeJWSHead(r *http.Request) (*jwsHead, error) {
var req struct{ Protected string }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
b, err := base64.RawURLEncoding.DecodeString(req.Protected)
if err != nil {
return nil, err
}
var head jwsHead
if err := json.Unmarshal(b, &head); err != nil {
return nil, err
}
return &head, nil
}
func TestDiscover(t *testing.T) { func TestDiscover(t *testing.T) {
const ( const (
reg = "https://example.com/acme/new-reg" reg = "https://example.com/acme/new-reg"
@ -522,6 +543,9 @@ func TestWaitAuthorizationInvalid(t *testing.T) {
if err == nil { if err == nil {
t.Error("err is nil") t.Error("err is nil")
} }
if _, ok := err.(*AuthorizationError); !ok {
t.Errorf("err is %T; want *AuthorizationError", err)
}
} }
} }
@ -916,7 +940,30 @@ func TestRevokeCert(t *testing.T) {
} }
} }
func TestFetchNonce(t *testing.T) { func TestNonce_add(t *testing.T) {
var c Client
c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
c.addNonce(http.Header{"Replay-Nonce": {}})
c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
nonces := map[string]struct{}{"nonce": struct{}{}}
if !reflect.DeepEqual(c.nonces, nonces) {
t.Errorf("c.nonces = %q; want %q", c.nonces, nonces)
}
}
func TestNonce_addMax(t *testing.T) {
c := &Client{nonces: make(map[string]struct{})}
for i := 0; i < maxNonces; i++ {
c.nonces[fmt.Sprintf("%d", i)] = struct{}{}
}
c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
if n := len(c.nonces); n != maxNonces {
t.Errorf("len(c.nonces) = %d; want %d", n, maxNonces)
}
}
func TestNonce_fetch(t *testing.T) {
tests := []struct { tests := []struct {
code int code int
nonce string nonce string
@ -936,7 +983,8 @@ func TestFetchNonce(t *testing.T) {
defer ts.Close() defer ts.Close()
for ; i < len(tests); i++ { for ; i < len(tests); i++ {
test := tests[i] test := tests[i]
n, err := fetchNonce(context.Background(), http.DefaultClient, ts.URL) c := &Client{}
n, err := c.fetchNonce(context.Background(), ts.URL)
if n != test.nonce { if n != test.nonce {
t.Errorf("%d: n=%q; want %q", i, n, test.nonce) t.Errorf("%d: n=%q; want %q", i, n, test.nonce)
} }
@ -949,6 +997,115 @@ func TestFetchNonce(t *testing.T) {
} }
} }
func TestNonce_fetchError(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTooManyRequests)
}))
defer ts.Close()
c := &Client{}
_, err := c.fetchNonce(context.Background(), ts.URL)
e, ok := err.(*Error)
if !ok {
t.Fatalf("err is %T; want *Error", err)
}
if e.StatusCode != http.StatusTooManyRequests {
t.Errorf("e.StatusCode = %d; want %d", e.StatusCode, http.StatusTooManyRequests)
}
}
func TestNonce_postJWS(t *testing.T) {
var count int
seen := make(map[string]bool)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
w.Header().Set("replay-nonce", fmt.Sprintf("nonce%d", count))
if r.Method == "HEAD" {
// We expect the client do a HEAD request
// but only to fetch the first nonce.
return
}
// Make client.Authorize happy; we're not testing its result.
defer func() {
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"status":"valid"}`))
}()
head, err := decodeJWSHead(r)
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
return
}
if head.Nonce == "" {
t.Error("head.Nonce is empty")
return
}
if seen[head.Nonce] {
t.Errorf("nonce is already used: %q", head.Nonce)
}
seen[head.Nonce] = true
}))
defer ts.Close()
client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}}
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 1: %v", err)
}
// The second call should not generate another extra HEAD request.
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 2: %v", err)
}
if count != 3 {
t.Errorf("total requests count: %d; want 3", count)
}
if n := len(client.nonces); n != 1 {
t.Errorf("len(client.nonces) = %d; want 1", n)
}
for k := range seen {
if _, exist := client.nonces[k]; exist {
t.Errorf("used nonce %q in client.nonces", k)
}
}
}
func TestRetryPostJWS(t *testing.T) {
var count int
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
w.Header().Set("replay-nonce", fmt.Sprintf("nonce%d", count))
if r.Method == "HEAD" {
// We expect the client to do 2 head requests to fetch
// nonces, one to start and another after getting badNonce
return
}
head, err := decodeJWSHead(r)
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
} else if head.Nonce == "" {
t.Error("head.Nonce is empty")
} else if head.Nonce == "nonce1" {
// return a badNonce error to force the call to retry
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"type":"urn:ietf:params:acme:error:badNonce"}`))
return
}
// Make client.Authorize happy; we're not testing its result.
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"status":"valid"}`))
}))
defer ts.Close()
client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}}
// This call will fail with badNonce, causing a retry
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 1: %v", err)
}
if count != 4 {
t.Errorf("total requests count: %d; want 4", count)
}
}
func TestLinkHeader(t *testing.T) { func TestLinkHeader(t *testing.T) {
h := http.Header{"Link": { h := http.Header{"Link": {
`<https://example.com/acme/new-authz>;rel="next"`, `<https://example.com/acme/new-authz>;rel="next"`,

View file

@ -10,6 +10,7 @@ package autocert
import ( import (
"bytes" "bytes"
"context"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
@ -30,9 +31,14 @@ import (
"time" "time"
"golang.org/x/crypto/acme" "golang.org/x/crypto/acme"
"golang.org/x/net/context"
) )
// createCertRetryAfter is how much time to wait before removing a failed state
// entry due to an unsuccessful createCert call.
// This is a variable instead of a const for testing.
// TODO: Consider making it configurable or an exp backoff?
var createCertRetryAfter = time.Minute
// pseudoRand is safe for concurrent use. // pseudoRand is safe for concurrent use.
var pseudoRand *lockedMathRand var pseudoRand *lockedMathRand
@ -41,8 +47,9 @@ func init() {
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)} pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
} }
// AcceptTOS always returns true to indicate the acceptance of a CA Terms of Service // AcceptTOS is a Manager.Prompt function that always returns true to
// during account registration. // indicate acceptance of the CA's Terms of Service during account
// registration.
func AcceptTOS(tosURL string) bool { return true } func AcceptTOS(tosURL string) bool { return true }
// HostPolicy specifies which host names the Manager is allowed to respond to. // HostPolicy specifies which host names the Manager is allowed to respond to.
@ -76,18 +83,6 @@ func defaultHostPolicy(context.Context, string) error {
// It obtains and refreshes certificates automatically, // It obtains and refreshes certificates automatically,
// as well as providing them to a TLS server via tls.Config. // as well as providing them to a TLS server via tls.Config.
// //
// A simple usage example:
//
// m := autocert.Manager{
// Prompt: autocert.AcceptTOS,
// HostPolicy: autocert.HostWhitelist("example.org"),
// }
// s := &http.Server{
// Addr: ":https",
// TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
// }
// s.ListenAndServeTLS("", "")
//
// To preserve issued certificates and improve overall performance, // To preserve issued certificates and improve overall performance,
// use a cache implementation of Cache. For instance, DirCache. // use a cache implementation of Cache. For instance, DirCache.
type Manager struct { type Manager struct {
@ -123,7 +118,7 @@ type Manager struct {
// RenewBefore optionally specifies how early certificates should // RenewBefore optionally specifies how early certificates should
// be renewed before they expire. // be renewed before they expire.
// //
// If zero, they're renewed 1 week before expiration. // If zero, they're renewed 30 days before expiration.
RenewBefore time.Duration RenewBefore time.Duration
// Client is used to perform low-level operations, such as account registration // Client is used to perform low-level operations, such as account registration
@ -173,10 +168,23 @@ type Manager struct {
// The error is propagated back to the caller of GetCertificate and is user-visible. // The error is propagated back to the caller of GetCertificate and is user-visible.
// This does not affect cached certs. See HostPolicy field description for more details. // This does not affect cached certs. See HostPolicy field description for more details.
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
if m.Prompt == nil {
return nil, errors.New("acme/autocert: Manager.Prompt not set")
}
name := hello.ServerName name := hello.ServerName
if name == "" { if name == "" {
return nil, errors.New("acme/autocert: missing server name") return nil, errors.New("acme/autocert: missing server name")
} }
if !strings.Contains(strings.Trim(name, "."), ".") {
return nil, errors.New("acme/autocert: server name component count invalid")
}
if strings.ContainsAny(name, `/\`) {
return nil, errors.New("acme/autocert: server name contains invalid character")
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// check whether this is a token cert requested for TLS-SNI challenge // check whether this is a token cert requested for TLS-SNI challenge
if strings.HasSuffix(name, ".acme.invalid") { if strings.HasSuffix(name, ".acme.invalid") {
@ -185,7 +193,7 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
if cert := m.tokenCert[name]; cert != nil { if cert := m.tokenCert[name]; cert != nil {
return cert, nil return cert, nil
} }
if cert, err := m.cacheGet(name); err == nil { if cert, err := m.cacheGet(ctx, name); err == nil {
return cert, nil return cert, nil
} }
// TODO: cache error results? // TODO: cache error results?
@ -194,7 +202,7 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
// regular domain // regular domain
name = strings.TrimSuffix(name, ".") // golang.org/issue/18114 name = strings.TrimSuffix(name, ".") // golang.org/issue/18114
cert, err := m.cert(name) cert, err := m.cert(ctx, name)
if err == nil { if err == nil {
return cert, nil return cert, nil
} }
@ -203,7 +211,6 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
} }
// first-time // first-time
ctx := context.Background() // TODO: use a deadline?
if err := m.hostPolicy()(ctx, name); err != nil { if err := m.hostPolicy()(ctx, name); err != nil {
return nil, err return nil, err
} }
@ -211,14 +218,14 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
if err != nil { if err != nil {
return nil, err return nil, err
} }
m.cachePut(name, cert) m.cachePut(ctx, name, cert)
return cert, nil return cert, nil
} }
// cert returns an existing certificate either from m.state or cache. // cert returns an existing certificate either from m.state or cache.
// If a certificate is found in cache but not in m.state, the latter will be filled // If a certificate is found in cache but not in m.state, the latter will be filled
// with the cached value. // with the cached value.
func (m *Manager) cert(name string) (*tls.Certificate, error) { func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) {
m.stateMu.Lock() m.stateMu.Lock()
if s, ok := m.state[name]; ok { if s, ok := m.state[name]; ok {
m.stateMu.Unlock() m.stateMu.Unlock()
@ -227,7 +234,7 @@ func (m *Manager) cert(name string) (*tls.Certificate, error) {
return s.tlscert() return s.tlscert()
} }
defer m.stateMu.Unlock() defer m.stateMu.Unlock()
cert, err := m.cacheGet(name) cert, err := m.cacheGet(ctx, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -249,12 +256,11 @@ func (m *Manager) cert(name string) (*tls.Certificate, error) {
} }
// cacheGet always returns a valid certificate, or an error otherwise. // cacheGet always returns a valid certificate, or an error otherwise.
func (m *Manager) cacheGet(domain string) (*tls.Certificate, error) { // If a cached certficate exists but is not valid, ErrCacheMiss is returned.
func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) {
if m.Cache == nil { if m.Cache == nil {
return nil, ErrCacheMiss return nil, ErrCacheMiss
} }
// TODO: might want to define a cache timeout on m
ctx := context.Background()
data, err := m.Cache.Get(ctx, domain) data, err := m.Cache.Get(ctx, domain)
if err != nil { if err != nil {
return nil, err return nil, err
@ -263,7 +269,7 @@ func (m *Manager) cacheGet(domain string) (*tls.Certificate, error) {
// private // private
priv, pub := pem.Decode(data) priv, pub := pem.Decode(data)
if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
return nil, errors.New("acme/autocert: no private key found in cache") return nil, ErrCacheMiss
} }
privKey, err := parsePrivateKey(priv.Bytes) privKey, err := parsePrivateKey(priv.Bytes)
if err != nil { if err != nil {
@ -281,13 +287,14 @@ func (m *Manager) cacheGet(domain string) (*tls.Certificate, error) {
pubDER = append(pubDER, b.Bytes) pubDER = append(pubDER, b.Bytes)
} }
if len(pub) > 0 { if len(pub) > 0 {
return nil, errors.New("acme/autocert: invalid public key") // Leftover content not consumed by pem.Decode. Corrupt. Ignore.
return nil, ErrCacheMiss
} }
// verify and create TLS cert // verify and create TLS cert
leaf, err := validCert(domain, pubDER, privKey) leaf, err := validCert(domain, pubDER, privKey)
if err != nil { if err != nil {
return nil, err return nil, ErrCacheMiss
} }
tlscert := &tls.Certificate{ tlscert := &tls.Certificate{
Certificate: pubDER, Certificate: pubDER,
@ -297,7 +304,7 @@ func (m *Manager) cacheGet(domain string) (*tls.Certificate, error) {
return tlscert, nil return tlscert, nil
} }
func (m *Manager) cachePut(domain string, tlscert *tls.Certificate) error { func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error {
if m.Cache == nil { if m.Cache == nil {
return nil return nil
} }
@ -329,8 +336,6 @@ func (m *Manager) cachePut(domain string, tlscert *tls.Certificate) error {
} }
} }
// TODO: might want to define a cache timeout on m
ctx := context.Background()
return m.Cache.Put(ctx, domain, buf.Bytes()) return m.Cache.Put(ctx, domain, buf.Bytes())
} }
@ -370,6 +375,23 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica
der, leaf, err := m.authorizedCert(ctx, state.key, domain) der, leaf, err := m.authorizedCert(ctx, state.key, domain)
if err != nil { if err != nil {
// Remove the failed state after some time,
// making the manager call createCert again on the following TLS hello.
time.AfterFunc(createCertRetryAfter, func() {
defer testDidRemoveState(domain)
m.stateMu.Lock()
defer m.stateMu.Unlock()
// Verify the state hasn't changed and it's still invalid
// before deleting.
s, ok := m.state[domain]
if !ok {
return
}
if _, err := validCert(domain, s.cert, s.key); err == nil {
return
}
delete(m.state, domain)
})
return nil, err return nil, err
} }
state.cert = der state.cert = der
@ -418,7 +440,6 @@ func (m *Manager) certState(domain string) (*certState, error) {
// authorizedCert starts domain ownership verification process and requests a new cert upon success. // authorizedCert starts domain ownership verification process and requests a new cert upon success.
// The key argument is the certificate private key. // The key argument is the certificate private key.
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) { func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
// TODO: make m.verify retry or retry m.verify calls here
if err := m.verify(ctx, domain); err != nil { if err := m.verify(ctx, domain); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -494,7 +515,7 @@ func (m *Manager) verify(ctx context.Context, domain string) error {
if err != nil { if err != nil {
return err return err
} }
m.putTokenCert(name, &cert) m.putTokenCert(ctx, name, &cert)
defer func() { defer func() {
// verification has ended at this point // verification has ended at this point
// don't need token cert anymore // don't need token cert anymore
@ -512,14 +533,14 @@ func (m *Manager) verify(ctx context.Context, domain string) error {
// putTokenCert stores the cert under the named key in both m.tokenCert map // putTokenCert stores the cert under the named key in both m.tokenCert map
// and m.Cache. // and m.Cache.
func (m *Manager) putTokenCert(name string, cert *tls.Certificate) { func (m *Manager) putTokenCert(ctx context.Context, name string, cert *tls.Certificate) {
m.tokenCertMu.Lock() m.tokenCertMu.Lock()
defer m.tokenCertMu.Unlock() defer m.tokenCertMu.Unlock()
if m.tokenCert == nil { if m.tokenCert == nil {
m.tokenCert = make(map[string]*tls.Certificate) m.tokenCert = make(map[string]*tls.Certificate)
} }
m.tokenCert[name] = cert m.tokenCert[name] = cert
m.cachePut(name, cert) m.cachePut(ctx, name, cert)
} }
// deleteTokenCert removes the token certificate for the specified domain name // deleteTokenCert removes the token certificate for the specified domain name
@ -644,10 +665,10 @@ func (m *Manager) hostPolicy() HostPolicy {
} }
func (m *Manager) renewBefore() time.Duration { func (m *Manager) renewBefore() time.Duration {
if m.RenewBefore > maxRandRenew { if m.RenewBefore > renewJitter {
return m.RenewBefore return m.RenewBefore
} }
return 7 * 24 * time.Hour // 1 week return 720 * time.Hour // 30 days
} }
// certState is ready when its mutex is unlocked for reading. // certState is ready when its mutex is unlocked for reading.
@ -789,5 +810,10 @@ func (r *lockedMathRand) int63n(max int64) int64 {
return n return n
} }
// for easier testing // For easier testing.
var timeNow = time.Now var (
timeNow = time.Now
// Called when a state is removed.
testDidRemoveState = func(domain string) {}
)

View file

@ -5,6 +5,7 @@
package autocert package autocert
import ( import (
"context"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
@ -22,11 +23,11 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"reflect" "reflect"
"sync"
"testing" "testing"
"time" "time"
"golang.org/x/crypto/acme" "golang.org/x/crypto/acme"
"golang.org/x/net/context"
) )
var discoTmpl = template.Must(template.New("disco").Parse(`{ var discoTmpl = template.Must(template.New("disco").Parse(`{
@ -51,26 +52,44 @@ var authzTmpl = template.Must(template.New("authz").Parse(`{
] ]
}`)) }`))
type memCache map[string][]byte type memCache struct {
mu sync.Mutex
keyData map[string][]byte
}
func (m memCache) Get(ctx context.Context, key string) ([]byte, error) { func (m *memCache) Get(ctx context.Context, key string) ([]byte, error) {
v, ok := m[key] m.mu.Lock()
defer m.mu.Unlock()
v, ok := m.keyData[key]
if !ok { if !ok {
return nil, ErrCacheMiss return nil, ErrCacheMiss
} }
return v, nil return v, nil
} }
func (m memCache) Put(ctx context.Context, key string, data []byte) error { func (m *memCache) Put(ctx context.Context, key string, data []byte) error {
m[key] = data m.mu.Lock()
defer m.mu.Unlock()
m.keyData[key] = data
return nil return nil
} }
func (m memCache) Delete(ctx context.Context, key string) error { func (m *memCache) Delete(ctx context.Context, key string) error {
delete(m, key) m.mu.Lock()
defer m.mu.Unlock()
delete(m.keyData, key)
return nil return nil
} }
func newMemCache() *memCache {
return &memCache{
keyData: make(map[string][]byte),
}
}
func dummyCert(pub interface{}, san ...string) ([]byte, error) { func dummyCert(pub interface{}, san ...string) ([]byte, error) {
return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...) return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...)
} }
@ -124,14 +143,14 @@ func TestGetCertificate_trailingDot(t *testing.T) {
func TestGetCertificate_ForceRSA(t *testing.T) { func TestGetCertificate_ForceRSA(t *testing.T) {
man := &Manager{ man := &Manager{
Prompt: AcceptTOS, Prompt: AcceptTOS,
Cache: make(memCache), Cache: newMemCache(),
ForceRSA: true, ForceRSA: true,
} }
defer man.stopRenew() defer man.stopRenew()
hello := &tls.ClientHelloInfo{ServerName: "example.org"} hello := &tls.ClientHelloInfo{ServerName: "example.org"}
testGetCertificate(t, man, "example.org", hello) testGetCertificate(t, man, "example.org", hello)
cert, err := man.cacheGet("example.org") cert, err := man.cacheGet(context.Background(), "example.org")
if err != nil { if err != nil {
t.Fatalf("man.cacheGet: %v", err) t.Fatalf("man.cacheGet: %v", err)
} }
@ -140,9 +159,110 @@ func TestGetCertificate_ForceRSA(t *testing.T) {
} }
} }
// tests man.GetCertificate flow using the provided hello argument. func TestGetCertificate_nilPrompt(t *testing.T) {
man := &Manager{}
defer man.stopRenew()
url, finish := startACMEServerStub(t, man, "example.org")
defer finish()
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
man.Client = &acme.Client{
Key: key,
DirectoryURL: url,
}
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
if _, err := man.GetCertificate(hello); err == nil {
t.Error("got certificate for example.org; wanted error")
}
}
func TestGetCertificate_expiredCache(t *testing.T) {
// Make an expired cert and cache it.
pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "example.org"},
NotAfter: time.Now(),
}
pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk)
if err != nil {
t.Fatal(err)
}
tlscert := &tls.Certificate{
Certificate: [][]byte{pub},
PrivateKey: pk,
}
man := &Manager{Prompt: AcceptTOS, Cache: newMemCache()}
defer man.stopRenew()
if err := man.cachePut(context.Background(), "example.org", tlscert); err != nil {
t.Fatalf("man.cachePut: %v", err)
}
// The expired cached cert should trigger a new cert issuance
// and return without an error.
hello := &tls.ClientHelloInfo{ServerName: "example.org"}
testGetCertificate(t, man, "example.org", hello)
}
func TestGetCertificate_failedAttempt(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
}))
defer ts.Close()
const example = "example.org"
d := createCertRetryAfter
f := testDidRemoveState
defer func() {
createCertRetryAfter = d
testDidRemoveState = f
}()
createCertRetryAfter = 0
done := make(chan struct{})
testDidRemoveState = func(domain string) {
if domain != example {
t.Errorf("testDidRemoveState: domain = %q; want %q", domain, example)
}
close(done)
}
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
man := &Manager{
Prompt: AcceptTOS,
Client: &acme.Client{
Key: key,
DirectoryURL: ts.URL,
},
}
defer man.stopRenew()
hello := &tls.ClientHelloInfo{ServerName: example}
if _, err := man.GetCertificate(hello); err == nil {
t.Error("GetCertificate: err is nil")
}
select {
case <-time.After(5 * time.Second):
t.Errorf("took too long to remove the %q state", example)
case <-done:
man.stateMu.Lock()
defer man.stateMu.Unlock()
if v, exist := man.state[example]; exist {
t.Errorf("state exists for %q: %+v", example, v)
}
}
}
// startACMEServerStub runs an ACME server
// The domain argument is the expected domain name of a certificate request. // The domain argument is the expected domain name of a certificate request.
func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) { func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string, finish func()) {
// echo token-02 | shasum -a 256 // echo token-02 | shasum -a 256
// then divide result in 2 parts separated by dot // then divide result in 2 parts separated by dot
tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid" tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid"
@ -168,7 +288,7 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
// discovery // discovery
case "/": case "/":
if err := discoTmpl.Execute(w, ca.URL); err != nil { if err := discoTmpl.Execute(w, ca.URL); err != nil {
t.Fatalf("discoTmpl: %v", err) t.Errorf("discoTmpl: %v", err)
} }
// client key registration // client key registration
case "/new-reg": case "/new-reg":
@ -178,7 +298,7 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
w.Header().Set("location", ca.URL+"/authz/1") w.Header().Set("location", ca.URL+"/authz/1")
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
if err := authzTmpl.Execute(w, ca.URL); err != nil { if err := authzTmpl.Execute(w, ca.URL); err != nil {
t.Fatalf("authzTmpl: %v", err) t.Errorf("authzTmpl: %v", err)
} }
// accept tls-sni-02 challenge // accept tls-sni-02 challenge
case "/challenge/2": case "/challenge/2":
@ -196,14 +316,14 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
b, _ := base64.RawURLEncoding.DecodeString(req.CSR) b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
csr, err := x509.ParseCertificateRequest(b) csr, err := x509.ParseCertificateRequest(b)
if err != nil { if err != nil {
t.Fatalf("new-cert: CSR: %v", err) t.Errorf("new-cert: CSR: %v", err)
} }
if csr.Subject.CommonName != domain { if csr.Subject.CommonName != domain {
t.Errorf("CommonName in CSR = %q; want %q", csr.Subject.CommonName, domain) t.Errorf("CommonName in CSR = %q; want %q", csr.Subject.CommonName, domain)
} }
der, err := dummyCert(csr.PublicKey, domain) der, err := dummyCert(csr.PublicKey, domain)
if err != nil { if err != nil {
t.Fatalf("new-cert: dummyCert: %v", err) t.Errorf("new-cert: dummyCert: %v", err)
} }
chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL) chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
w.Header().Set("link", chainUp) w.Header().Set("link", chainUp)
@ -213,14 +333,51 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
case "/ca-cert": case "/ca-cert":
der, err := dummyCert(nil, "ca") der, err := dummyCert(nil, "ca")
if err != nil { if err != nil {
t.Fatalf("ca-cert: dummyCert: %v", err) t.Errorf("ca-cert: dummyCert: %v", err)
} }
w.Write(der) w.Write(der)
default: default:
t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path) t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
} }
})) }))
defer ca.Close() finish = func() {
ca.Close()
// make sure token cert was removed
cancel := make(chan struct{})
done := make(chan struct{})
go func() {
defer close(done)
tick := time.NewTicker(100 * time.Millisecond)
defer tick.Stop()
for {
hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
if _, err := man.GetCertificate(hello); err != nil {
return
}
select {
case <-tick.C:
case <-cancel:
return
}
}
}()
select {
case <-done:
case <-time.After(5 * time.Second):
close(cancel)
t.Error("token cert was not removed")
<-done
}
}
return ca.URL, finish
}
// tests man.GetCertificate flow using the provided hello argument.
// The domain argument is the expected domain name of a certificate request.
func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) {
url, finish := startACMEServerStub(t, man, domain)
defer finish()
// use EC key to run faster on 386 // use EC key to run faster on 386
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@ -229,7 +386,7 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
} }
man.Client = &acme.Client{ man.Client = &acme.Client{
Key: key, Key: key,
DirectoryURL: ca.URL, DirectoryURL: url,
} }
// simulate tls.Config.GetCertificate // simulate tls.Config.GetCertificate
@ -260,28 +417,10 @@ func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.Cl
t.Errorf("cert.DNSNames = %v; want %q", cert.DNSNames, domain) t.Errorf("cert.DNSNames = %v; want %q", cert.DNSNames, domain)
} }
// make sure token cert was removed
done = make(chan struct{})
go func() {
for {
hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
if _, err := man.GetCertificate(hello); err != nil {
break
}
time.Sleep(100 * time.Millisecond)
}
close(done)
}()
select {
case <-time.After(5 * time.Second):
t.Error("token cert was not removed")
case <-done:
}
} }
func TestAccountKeyCache(t *testing.T) { func TestAccountKeyCache(t *testing.T) {
cache := make(memCache) m := Manager{Cache: newMemCache()}
m := Manager{Cache: cache}
ctx := context.Background() ctx := context.Background()
k1, err := m.accountKey(ctx) k1, err := m.accountKey(ctx)
if err != nil { if err != nil {
@ -315,13 +454,13 @@ func TestCache(t *testing.T) {
PrivateKey: privKey, PrivateKey: privKey,
} }
cache := make(memCache) man := &Manager{Cache: newMemCache()}
man := &Manager{Cache: cache}
defer man.stopRenew() defer man.stopRenew()
if err := man.cachePut("example.org", tlscert); err != nil { ctx := context.Background()
if err := man.cachePut(ctx, "example.org", tlscert); err != nil {
t.Fatalf("man.cachePut: %v", err) t.Fatalf("man.cachePut: %v", err)
} }
res, err := man.cacheGet("example.org") res, err := man.cacheGet(ctx, "example.org")
if err != nil { if err != nil {
t.Fatalf("man.cacheGet: %v", err) t.Fatalf("man.cacheGet: %v", err)
} }
@ -421,3 +560,47 @@ func TestValidCert(t *testing.T) {
} }
} }
} }
type cacheGetFunc func(ctx context.Context, key string) ([]byte, error)
func (f cacheGetFunc) Get(ctx context.Context, key string) ([]byte, error) {
return f(ctx, key)
}
func (f cacheGetFunc) Put(ctx context.Context, key string, data []byte) error {
return fmt.Errorf("unsupported Put of %q = %q", key, data)
}
func (f cacheGetFunc) Delete(ctx context.Context, key string) error {
return fmt.Errorf("unsupported Delete of %q", key)
}
func TestManagerGetCertificateBogusSNI(t *testing.T) {
m := Manager{
Prompt: AcceptTOS,
Cache: cacheGetFunc(func(ctx context.Context, key string) ([]byte, error) {
return nil, fmt.Errorf("cache.Get of %s", key)
}),
}
tests := []struct {
name string
wantErr string
}{
{"foo.com", "cache.Get of foo.com"},
{"foo.com.", "cache.Get of foo.com"},
{`a\b.com`, "acme/autocert: server name contains invalid character"},
{`a/b.com`, "acme/autocert: server name contains invalid character"},
{"", "acme/autocert: missing server name"},
{"foo", "acme/autocert: server name component count invalid"},
{".foo", "acme/autocert: server name component count invalid"},
{"foo.", "acme/autocert: server name component count invalid"},
{"fo.o", "cache.Get of fo.o"},
}
for _, tt := range tests {
_, err := m.GetCertificate(&tls.ClientHelloInfo{ServerName: tt.name})
got := fmt.Sprint(err)
if got != tt.wantErr {
t.Errorf("GetCertificate(SNI = %q) = %q; want %q", tt.name, got, tt.wantErr)
}
}
}

View file

@ -5,12 +5,11 @@
package autocert package autocert
import ( import (
"context"
"errors" "errors"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"golang.org/x/net/context"
) )
// ErrCacheMiss is returned when a certificate is not found in cache. // ErrCacheMiss is returned when a certificate is not found in cache.
@ -78,12 +77,13 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
if tmp, err = d.writeTempFile(name, data); err != nil { if tmp, err = d.writeTempFile(name, data); err != nil {
return return
} }
// prevent overwriting the file if the context was cancelled select {
if ctx.Err() != nil { case <-ctx.Done():
return // no need to set err // Don't overwrite the file if the context was canceled.
default:
newName := filepath.Join(string(d), name)
err = os.Rename(tmp, newName)
} }
name = filepath.Join(string(d), name)
err = os.Rename(tmp, name)
}() }()
select { select {
case <-ctx.Done(): case <-ctx.Done():

View file

@ -5,13 +5,12 @@
package autocert package autocert
import ( import (
"context"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
"golang.org/x/net/context"
) )
// make sure DirCache satisfies Cache interface // make sure DirCache satisfies Cache interface

View file

@ -0,0 +1,34 @@
// Copyright 2017 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 autocert_test
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"golang.org/x/crypto/acme/autocert"
)
func ExampleNewListener() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS)
})
log.Fatal(http.Serve(autocert.NewListener("example.com"), mux))
}
func ExampleManager() {
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example.org"),
}
s := &http.Server{
Addr: ":https",
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
}
s.ListenAndServeTLS("", "")
}

160
vendor/golang.org/x/crypto/acme/autocert/listener.go generated vendored Normal file
View file

@ -0,0 +1,160 @@
// Copyright 2017 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 autocert
import (
"crypto/tls"
"log"
"net"
"os"
"path/filepath"
"runtime"
"time"
)
// NewListener returns a net.Listener that listens on the standard TLS
// port (443) on all interfaces and returns *tls.Conn connections with
// LetsEncrypt certificates for the provided domain or domains.
//
// It enables one-line HTTPS servers:
//
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
//
// NewListener is a convenience function for a common configuration.
// More complex or custom configurations can use the autocert.Manager
// type instead.
//
// Use of this function implies acceptance of the LetsEncrypt Terms of
// Service. If domains is not empty, the provided domains are passed
// to HostWhitelist. If domains is empty, the listener will do
// LetsEncrypt challenges for any requested domain, which is not
// recommended.
//
// Certificates are cached in a "golang-autocert" directory under an
// operating system-specific cache or temp directory. This may not
// be suitable for servers spanning multiple machines.
//
// The returned listener uses a *tls.Config that enables HTTP/2, and
// should only be used with servers that support HTTP/2.
//
// The returned Listener also enables TCP keep-alives on the accepted
// connections. The returned *tls.Conn are returned before their TLS
// handshake has completed.
func NewListener(domains ...string) net.Listener {
m := &Manager{
Prompt: AcceptTOS,
}
if len(domains) > 0 {
m.HostPolicy = HostWhitelist(domains...)
}
dir := cacheDir()
if err := os.MkdirAll(dir, 0700); err != nil {
log.Printf("warning: autocert.NewListener not using a cache: %v", err)
} else {
m.Cache = DirCache(dir)
}
return m.Listener()
}
// Listener listens on the standard TLS port (443) on all interfaces
// and returns a net.Listener returning *tls.Conn connections.
//
// The returned listener uses a *tls.Config that enables HTTP/2, and
// should only be used with servers that support HTTP/2.
//
// The returned Listener also enables TCP keep-alives on the accepted
// connections. The returned *tls.Conn are returned before their TLS
// handshake has completed.
//
// Unlike NewListener, it is the caller's responsibility to initialize
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
func (m *Manager) Listener() net.Listener {
ln := &listener{
m: m,
conf: &tls.Config{
GetCertificate: m.GetCertificate, // bonus: panic on nil m
NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2
},
}
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
return ln
}
type listener struct {
m *Manager
conf *tls.Config
tcpListener net.Listener
tcpListenErr error
}
func (ln *listener) Accept() (net.Conn, error) {
if ln.tcpListenErr != nil {
return nil, ln.tcpListenErr
}
conn, err := ln.tcpListener.Accept()
if err != nil {
return nil, err
}
tcpConn := conn.(*net.TCPConn)
// Because Listener is a convenience function, help out with
// this too. This is not possible for the caller to set once
// we return a *tcp.Conn wrapping an inaccessible net.Conn.
// If callers don't want this, they can do things the manual
// way and tweak as needed. But this is what net/http does
// itself, so copy that. If net/http changes, we can change
// here too.
tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(3 * time.Minute)
return tls.Server(tcpConn, ln.conf), nil
}
func (ln *listener) Addr() net.Addr {
if ln.tcpListener != nil {
return ln.tcpListener.Addr()
}
// net.Listen failed. Return something non-nil in case callers
// call Addr before Accept:
return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443}
}
func (ln *listener) Close() error {
if ln.tcpListenErr != nil {
return ln.tcpListenErr
}
return ln.tcpListener.Close()
}
func homeDir() string {
if runtime.GOOS == "windows" {
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
}
if h := os.Getenv("HOME"); h != "" {
return h
}
return "/"
}
func cacheDir() string {
const base = "golang-autocert"
switch runtime.GOOS {
case "darwin":
return filepath.Join(homeDir(), "Library", "Caches", base)
case "windows":
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
if v := os.Getenv(ev); v != "" {
return filepath.Join(v, base)
}
}
// Worst case:
return filepath.Join(homeDir(), base)
}
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
return filepath.Join(xdg, base)
}
return filepath.Join(homeDir(), ".cache", base)
}

View file

@ -5,15 +5,14 @@
package autocert package autocert
import ( import (
"context"
"crypto" "crypto"
"sync" "sync"
"time" "time"
"golang.org/x/net/context"
) )
// maxRandRenew is a maximum deviation from Manager.RenewBefore. // renewJitter is the maximum deviation from Manager.RenewBefore.
const maxRandRenew = time.Hour const renewJitter = time.Hour
// domainRenewal tracks the state used by the periodic timers // domainRenewal tracks the state used by the periodic timers
// renewing a single domain's cert. // renewing a single domain's cert.
@ -65,7 +64,7 @@ func (dr *domainRenewal) renew() {
// TODO: rotate dr.key at some point? // TODO: rotate dr.key at some point?
next, err := dr.do(ctx) next, err := dr.do(ctx)
if err != nil { if err != nil {
next = maxRandRenew / 2 next = renewJitter / 2
next += time.Duration(pseudoRand.int63n(int64(next))) next += time.Duration(pseudoRand.int63n(int64(next)))
} }
dr.timer = time.AfterFunc(next, dr.renew) dr.timer = time.AfterFunc(next, dr.renew)
@ -83,9 +82,9 @@ func (dr *domainRenewal) renew() {
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
// a race is likely unavoidable in a distributed environment // a race is likely unavoidable in a distributed environment
// but we try nonetheless // but we try nonetheless
if tlscert, err := dr.m.cacheGet(dr.domain); err == nil { if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil {
next := dr.next(tlscert.Leaf.NotAfter) next := dr.next(tlscert.Leaf.NotAfter)
if next > dr.m.renewBefore()+maxRandRenew { if next > dr.m.renewBefore()+renewJitter {
return next, nil return next, nil
} }
} }
@ -103,7 +102,7 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
dr.m.cachePut(dr.domain, tlscert) dr.m.cachePut(ctx, dr.domain, tlscert)
dr.m.stateMu.Lock() dr.m.stateMu.Lock()
defer dr.m.stateMu.Unlock() defer dr.m.stateMu.Unlock()
// m.state is guaranteed to be non-nil at this point // m.state is guaranteed to be non-nil at this point
@ -114,7 +113,7 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
func (dr *domainRenewal) next(expiry time.Time) time.Duration { func (dr *domainRenewal) next(expiry time.Time) time.Duration {
d := expiry.Sub(timeNow()) - dr.m.renewBefore() d := expiry.Sub(timeNow()) - dr.m.renewBefore()
// add a bit of randomness to renew deadline // add a bit of randomness to renew deadline
n := pseudoRand.int63n(int64(maxRandRenew)) n := pseudoRand.int63n(int64(renewJitter))
d -= time.Duration(n) d -= time.Duration(n)
if d < 0 { if d < 0 {
return 0 return 0

View file

@ -5,6 +5,7 @@
package autocert package autocert
import ( import (
"context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
@ -31,7 +32,7 @@ func TestRenewalNext(t *testing.T) {
expiry time.Time expiry time.Time
min, max time.Duration min, max time.Duration
}{ }{
{now.Add(90 * 24 * time.Hour), 83*24*time.Hour - maxRandRenew, 83 * 24 * time.Hour}, {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
{now.Add(time.Hour), 0, 1}, {now.Add(time.Hour), 0, 1},
{now, 0, 1}, {now, 0, 1},
{now.Add(-time.Hour), 0, 1}, {now.Add(-time.Hour), 0, 1},
@ -111,7 +112,7 @@ func TestRenewFromCache(t *testing.T) {
} }
man := &Manager{ man := &Manager{
Prompt: AcceptTOS, Prompt: AcceptTOS,
Cache: make(memCache), Cache: newMemCache(),
RenewBefore: 24 * time.Hour, RenewBefore: 24 * time.Hour,
Client: &acme.Client{ Client: &acme.Client{
Key: key, Key: key,
@ -127,7 +128,7 @@ func TestRenewFromCache(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}} tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
if err := man.cachePut(domain, tlscert); err != nil { if err := man.cachePut(context.Background(), domain, tlscert); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -151,7 +152,7 @@ func TestRenewFromCache(t *testing.T) {
// ensure the new cert is cached // ensure the new cert is cached
after := time.Now().Add(future) after := time.Now().Add(future)
tlscert, err := man.cacheGet(domain) tlscert, err := man.cacheGet(context.Background(), domain)
if err != nil { if err != nil {
t.Fatalf("man.cacheGet: %v", err) t.Fatalf("man.cacheGet: %v", err)
} }

View file

@ -134,7 +134,7 @@ func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
return "ES256", crypto.SHA256 return "ES256", crypto.SHA256
case "P-384": case "P-384":
return "ES384", crypto.SHA384 return "ES384", crypto.SHA384
case "P-512": case "P-521":
return "ES512", crypto.SHA512 return "ES512", crypto.SHA512
} }
} }

View file

@ -12,11 +12,13 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"fmt"
"math/big" "math/big"
"testing" "testing"
) )
const testKeyPEM = ` const (
testKeyPEM = `
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq
WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30 WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30
@ -46,10 +48,9 @@ EQeIP6dZtv8IMgtGIb91QX9pXvP0aznzQKwYIA8nZgoENCPfiMTPiEDT9e/0lObO
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
` `
// This thumbprint is for the testKey defined above. // This thumbprint is for the testKey defined above.
const testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ" testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ"
const (
// openssl ecparam -name secp256k1 -genkey -noout // openssl ecparam -name secp256k1 -genkey -noout
testKeyECPEM = ` testKeyECPEM = `
-----BEGIN EC PRIVATE KEY----- -----BEGIN EC PRIVATE KEY-----
@ -58,39 +59,78 @@ AwEHoUQDQgAE5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HThqIrvawF5
QAaS/RNouybCiRhRjI3EaxLkQwgrCw0gqQ== QAaS/RNouybCiRhRjI3EaxLkQwgrCw0gqQ==
-----END EC PRIVATE KEY----- -----END EC PRIVATE KEY-----
` `
// 1. opnessl ec -in key.pem -noout -text // openssl ecparam -name secp384r1 -genkey -noout
testKeyEC384PEM = `
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAQ4lNtXRORWr1bgKR1CGysr9AJ9SyEk4jiVnlUWWUChmSNL+i9SLSD
Oe/naPqXJ6CgBwYFK4EEACKhZANiAAQzKtj+Ms0vHoTX5dzv3/L5YMXOWuI5UKRj
JigpahYCqXD2BA1j0E/2xt5vlPf+gm0PL+UHSQsCokGnIGuaHCsJAp3ry0gHQEke
WYXapUUFdvaK1R2/2hn5O+eiQM8YzCg=
-----END EC PRIVATE KEY-----
`
// openssl ecparam -name secp521r1 -genkey -noout
testKeyEC512PEM = `
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBSNZKFcWzXzB/aJClAb305ibalKgtDA7+70eEkdPt28/3LZMM935Z
KqYHh/COcxuu3Kt8azRAUz3gyr4zZKhlKUSgBwYFK4EEACOhgYkDgYYABAHUNKbx
7JwC7H6pa2sV0tERWhHhB3JmW+OP6SUgMWryvIKajlx73eS24dy4QPGrWO9/ABsD
FqcRSkNVTXnIv6+0mAF25knqIBIg5Q8M9BnOu9GGAchcwt3O7RDHmqewnJJDrbjd
GGnm6rb+NnWR9DIopM0nKNkToWoF/hzopxu4Ae/GsQ==
-----END EC PRIVATE KEY-----
`
// 1. openssl ec -in key.pem -noout -text
// 2. remove first byte, 04 (the header); the rest is X and Y // 2. remove first byte, 04 (the header); the rest is X and Y
// 3. covert each with: echo <val> | xxd -r -p | base64 | tr -d '=' | tr '/+' '_-' // 3. convert each with: echo <val> | xxd -r -p | base64 -w 100 | tr -d '=' | tr '/+' '_-'
testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ" testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ"
testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk" testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk"
testKeyEC384PubX = "MyrY_jLNLx6E1-Xc79_y-WDFzlriOVCkYyYoKWoWAqlw9gQNY9BP9sbeb5T3_oJt"
testKeyEC384PubY = "Dy_lB0kLAqJBpyBrmhwrCQKd68tIB0BJHlmF2qVFBXb2itUdv9oZ-TvnokDPGMwo"
testKeyEC512PubX = "AdQ0pvHsnALsfqlraxXS0RFaEeEHcmZb44_pJSAxavK8gpqOXHvd5Lbh3LhA8atY738AGwMWpxFKQ1VNeci_r7SY"
testKeyEC512PubY = "AXbmSeogEiDlDwz0Gc670YYByFzC3c7tEMeap7CckkOtuN0Yaebqtv42dZH0MiikzSco2ROhagX-HOinG7gB78ax"
// echo -n '{"crv":"P-256","kty":"EC","x":"<testKeyECPubX>","y":"<testKeyECPubY>"}' | \ // echo -n '{"crv":"P-256","kty":"EC","x":"<testKeyECPubX>","y":"<testKeyECPubY>"}' | \
// openssl dgst -binary -sha256 | base64 | tr -d '=' | tr '/+' '_-' // openssl dgst -binary -sha256 | base64 | tr -d '=' | tr '/+' '_-'
testKeyECThumbprint = "zedj-Bd1Zshp8KLePv2MB-lJ_Hagp7wAwdkA0NUTniU" testKeyECThumbprint = "zedj-Bd1Zshp8KLePv2MB-lJ_Hagp7wAwdkA0NUTniU"
) )
var ( var (
testKey *rsa.PrivateKey testKey *rsa.PrivateKey
testKeyEC *ecdsa.PrivateKey testKeyEC *ecdsa.PrivateKey
testKeyEC384 *ecdsa.PrivateKey
testKeyEC512 *ecdsa.PrivateKey
) )
func init() { func init() {
d, _ := pem.Decode([]byte(testKeyPEM)) testKey = parseRSA(testKeyPEM, "testKeyPEM")
if d == nil { testKeyEC = parseEC(testKeyECPEM, "testKeyECPEM")
panic("no block found in testKeyPEM") testKeyEC384 = parseEC(testKeyEC384PEM, "testKeyEC384PEM")
} testKeyEC512 = parseEC(testKeyEC512PEM, "testKeyEC512PEM")
var err error }
testKey, err = x509.ParsePKCS1PrivateKey(d.Bytes)
if err != nil {
panic(err.Error())
}
if d, _ = pem.Decode([]byte(testKeyECPEM)); d == nil { func decodePEM(s, name string) []byte {
panic("no block found in testKeyECPEM") d, _ := pem.Decode([]byte(s))
if d == nil {
panic("no block found in " + name)
} }
testKeyEC, err = x509.ParseECPrivateKey(d.Bytes) return d.Bytes
}
func parseRSA(s, name string) *rsa.PrivateKey {
b := decodePEM(s, name)
k, err := x509.ParsePKCS1PrivateKey(b)
if err != nil { if err != nil {
panic(err.Error()) panic(fmt.Sprintf("%s: %v", name, err))
} }
return k
}
func parseEC(s, name string) *ecdsa.PrivateKey {
b := decodePEM(s, name)
k, err := x509.ParseECPrivateKey(b)
if err != nil {
panic(fmt.Sprintf("%s: %v", name, err))
}
return k
} }
func TestJWSEncodeJSON(t *testing.T) { func TestJWSEncodeJSON(t *testing.T) {
@ -141,50 +181,63 @@ func TestJWSEncodeJSON(t *testing.T) {
} }
func TestJWSEncodeJSONEC(t *testing.T) { func TestJWSEncodeJSONEC(t *testing.T) {
claims := struct{ Msg string }{"Hello JWS"} tt := []struct {
key *ecdsa.PrivateKey
x, y string
alg, crv string
}{
{testKeyEC, testKeyECPubX, testKeyECPubY, "ES256", "P-256"},
{testKeyEC384, testKeyEC384PubX, testKeyEC384PubY, "ES384", "P-384"},
{testKeyEC512, testKeyEC512PubX, testKeyEC512PubY, "ES512", "P-521"},
}
for i, test := range tt {
claims := struct{ Msg string }{"Hello JWS"}
b, err := jwsEncodeJSON(claims, test.key, "nonce")
if err != nil {
t.Errorf("%d: %v", i, err)
continue
}
var jws struct{ Protected, Payload, Signature string }
if err := json.Unmarshal(b, &jws); err != nil {
t.Errorf("%d: %v", i, err)
continue
}
b, err := jwsEncodeJSON(claims, testKeyEC, "nonce") b, err = base64.RawURLEncoding.DecodeString(jws.Protected)
if err != nil { if err != nil {
t.Fatal(err) t.Errorf("%d: jws.Protected: %v", i, err)
} }
var jws struct{ Protected, Payload, Signature string } var head struct {
if err := json.Unmarshal(b, &jws); err != nil { Alg string
t.Fatal(err) Nonce string
} JWK struct {
Crv string
if b, err = base64.RawURLEncoding.DecodeString(jws.Protected); err != nil { Kty string
t.Fatalf("jws.Protected: %v", err) X string
} Y string
var head struct { } `json:"jwk"`
Alg string }
Nonce string if err := json.Unmarshal(b, &head); err != nil {
JWK struct { t.Errorf("%d: jws.Protected: %v", i, err)
Crv string }
Kty string if head.Alg != test.alg {
X string t.Errorf("%d: head.Alg = %q; want %q", i, head.Alg, test.alg)
Y string }
} `json:"jwk"` if head.Nonce != "nonce" {
} t.Errorf("%d: head.Nonce = %q; want nonce", i, head.Nonce)
if err := json.Unmarshal(b, &head); err != nil { }
t.Fatalf("jws.Protected: %v", err) if head.JWK.Crv != test.crv {
} t.Errorf("%d: head.JWK.Crv = %q; want %q", i, head.JWK.Crv, test.crv)
if head.Alg != "ES256" { }
t.Errorf("head.Alg = %q; want ES256", head.Alg) if head.JWK.Kty != "EC" {
} t.Errorf("%d: head.JWK.Kty = %q; want EC", i, head.JWK.Kty)
if head.Nonce != "nonce" { }
t.Errorf("head.Nonce = %q; want nonce", head.Nonce) if head.JWK.X != test.x {
} t.Errorf("%d: head.JWK.X = %q; want %q", i, head.JWK.X, test.x)
if head.JWK.Crv != "P-256" { }
t.Errorf("head.JWK.Crv = %q; want P-256", head.JWK.Crv) if head.JWK.Y != test.y {
} t.Errorf("%d: head.JWK.Y = %q; want %q", i, head.JWK.Y, test.y)
if head.JWK.Kty != "EC" { }
t.Errorf("head.JWK.Kty = %q; want EC", head.JWK.Kty)
}
if head.JWK.X != testKeyECPubX {
t.Errorf("head.JWK.X = %q; want %q", head.JWK.X, testKeyECPubX)
}
if head.JWK.Y != testKeyECPubY {
t.Errorf("head.JWK.Y = %q; want %q", head.JWK.Y, testKeyECPubY)
} }
} }

View file

@ -1,9 +1,15 @@
// Copyright 2016 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 acme package acme
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"time"
) )
// ACME server response statuses used to describe Authorization and Challenge states. // ACME server response statuses used to describe Authorization and Challenge states.
@ -33,14 +39,8 @@ const (
CRLReasonAACompromise CRLReasonCode = 10 CRLReasonAACompromise CRLReasonCode = 10
) )
var ( // ErrUnsupportedKey is returned when an unsupported key type is encountered.
// ErrAuthorizationFailed indicates that an authorization for an identifier var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
// did not succeed.
ErrAuthorizationFailed = errors.New("acme: identifier authorization failed")
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
)
// Error is an ACME error, defined in Problem Details for HTTP APIs doc // Error is an ACME error, defined in Problem Details for HTTP APIs doc
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem. // http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
@ -53,6 +53,7 @@ type Error struct {
// Detail is a human-readable explanation specific to this occurrence of the problem. // Detail is a human-readable explanation specific to this occurrence of the problem.
Detail string Detail string
// Header is the original server error response headers. // Header is the original server error response headers.
// It may be nil.
Header http.Header Header http.Header
} }
@ -60,6 +61,50 @@ func (e *Error) Error() string {
return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail) return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail)
} }
// AuthorizationError indicates that an authorization for an identifier
// did not succeed.
// It contains all errors from Challenge items of the failed Authorization.
type AuthorizationError struct {
// URI uniquely identifies the failed Authorization.
URI string
// Identifier is an AuthzID.Value of the failed Authorization.
Identifier string
// Errors is a collection of non-nil error values of Challenge items
// of the failed Authorization.
Errors []error
}
func (a *AuthorizationError) Error() string {
e := make([]string, len(a.Errors))
for i, err := range a.Errors {
e[i] = err.Error()
}
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
}
// RateLimit reports whether err represents a rate limit error and
// any Retry-After duration returned by the server.
//
// See the following for more details on rate limiting:
// https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6
func RateLimit(err error) (time.Duration, bool) {
e, ok := err.(*Error)
if !ok {
return 0, false
}
// Some CA implementations may return incorrect values.
// Use case-insensitive comparison.
if !strings.HasSuffix(strings.ToLower(e.ProblemType), ":ratelimited") {
return 0, false
}
if e.Header == nil {
return 0, true
}
return retryAfter(e.Header.Get("Retry-After"), 0), true
}
// Account is a user account. It is associated with a private key. // Account is a user account. It is associated with a private key.
type Account struct { type Account struct {
// URI is the account unique ID, which is also a URL used to retrieve // URI is the account unique ID, which is also a URL used to retrieve
@ -118,6 +163,8 @@ type Directory struct {
} }
// Challenge encodes a returned CA challenge. // Challenge encodes a returned CA challenge.
// Its Error field may be non-nil if the challenge is part of an Authorization
// with StatusInvalid.
type Challenge struct { type Challenge struct {
// Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01". // Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01".
Type string Type string
@ -130,6 +177,11 @@ type Challenge struct {
// Status identifies the status of this challenge. // Status identifies the status of this challenge.
Status string Status string
// Error indicates the reason for an authorization failure
// when this challenge was used.
// The type of a non-nil value is *Error.
Error error
} }
// Authorization encodes an authorization response. // Authorization encodes an authorization response.
@ -187,12 +239,26 @@ func (z *wireAuthz) authorization(uri string) *Authorization {
return a return a
} }
func (z *wireAuthz) error(uri string) *AuthorizationError {
err := &AuthorizationError{
URI: uri,
Identifier: z.Identifier.Value,
}
for _, raw := range z.Challenges {
if raw.Error != nil {
err.Errors = append(err.Errors, raw.Error.error(nil))
}
}
return err
}
// wireChallenge is ACME JSON challenge representation. // wireChallenge is ACME JSON challenge representation.
type wireChallenge struct { type wireChallenge struct {
URI string `json:"uri"` URI string `json:"uri"`
Type string Type string
Token string Token string
Status string Status string
Error *wireError
} }
func (c *wireChallenge) challenge() *Challenge { func (c *wireChallenge) challenge() *Challenge {
@ -205,5 +271,25 @@ func (c *wireChallenge) challenge() *Challenge {
if v.Status == "" { if v.Status == "" {
v.Status = StatusPending v.Status = StatusPending
} }
if c.Error != nil {
v.Error = c.Error.error(nil)
}
return v return v
} }
// wireError is a subset of fields of the Problem Details object
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
type wireError struct {
Status int
Type string
Detail string
}
func (e *wireError) error(h http.Header) *Error {
return &Error{
StatusCode: e.Status,
ProblemType: e.Type,
Detail: e.Detail,
Header: h,
}
}

63
vendor/golang.org/x/crypto/acme/types_test.go generated vendored Normal file
View file

@ -0,0 +1,63 @@
// Copyright 2017 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 acme
import (
"errors"
"net/http"
"testing"
"time"
)
func TestRateLimit(t *testing.T) {
now := time.Date(2017, 04, 27, 10, 0, 0, 0, time.UTC)
f := timeNow
defer func() { timeNow = f }()
timeNow = func() time.Time { return now }
h120, hTime := http.Header{}, http.Header{}
h120.Set("Retry-After", "120")
hTime.Set("Retry-After", "Tue Apr 27 11:00:00 2017")
err1 := &Error{
ProblemType: "urn:ietf:params:acme:error:nolimit",
Header: h120,
}
err2 := &Error{
ProblemType: "urn:ietf:params:acme:error:rateLimited",
Header: h120,
}
err3 := &Error{
ProblemType: "urn:ietf:params:acme:error:rateLimited",
Header: nil,
}
err4 := &Error{
ProblemType: "urn:ietf:params:acme:error:rateLimited",
Header: hTime,
}
tt := []struct {
err error
res time.Duration
ok bool
}{
{nil, 0, false},
{errors.New("dummy"), 0, false},
{err1, 0, false},
{err2, 2 * time.Minute, true},
{err3, 0, true},
{err4, time.Hour, true},
}
for i, test := range tt {
res, ok := RateLimit(test.err)
if ok != test.ok {
t.Errorf("%d: RateLimit(%+v): ok = %v; want %v", i, test.err, ok, test.ok)
continue
}
if res != test.res {
t.Errorf("%d: RateLimit(%+v) = %v; want %v", i, test.err, res, test.res)
}
}
}

View file

@ -12,9 +12,10 @@ import (
"crypto/subtle" "crypto/subtle"
"errors" "errors"
"fmt" "fmt"
"golang.org/x/crypto/blowfish"
"io" "io"
"strconv" "strconv"
"golang.org/x/crypto/blowfish"
) )
const ( const (
@ -205,7 +206,6 @@ func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
} }
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) { func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
csalt, err := base64Decode(salt) csalt, err := base64Decode(salt)
if err != nil { if err != nil {
return nil, err return nil, err
@ -213,7 +213,8 @@ func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cip
// Bug compatibility with C bcrypt implementations. They use the trailing // Bug compatibility with C bcrypt implementations. They use the trailing
// NULL in the key string during expansion. // NULL in the key string during expansion.
ckey := append(key, 0) // We copy the key to prevent changing the underlying array.
ckey := append(key[:len(key):len(key)], 0)
c, err := blowfish.NewSaltedCipher(ckey, csalt) c, err := blowfish.NewSaltedCipher(ckey, csalt)
if err != nil { if err != nil {

View file

@ -224,3 +224,20 @@ func BenchmarkGeneration(b *testing.B) {
GenerateFromPassword(passwd, 10) GenerateFromPassword(passwd, 10)
} }
} }
// See Issue https://github.com/golang/go/issues/20425.
func TestNoSideEffectsFromCompare(t *testing.T) {
source := []byte("passw0rd123456")
password := source[:len(source)-6]
token := source[len(source)-6:]
want := make([]byte, len(source))
copy(want, source)
wantHash := []byte("$2a$10$LK9XRuhNxHHCvjX3tdkRKei1QiCDUKrJRhZv7WWZPuQGRUM92rOUa")
_ = CompareHashAndPassword(wantHash, password)
got := bytes.Join([][]byte{password, token}, []byte(""))
if !bytes.Equal(got, want) {
t.Errorf("got=%q want=%q", got, want)
}
}

View file

@ -4,7 +4,7 @@
// Package blake2b implements the BLAKE2b hash algorithm as // Package blake2b implements the BLAKE2b hash algorithm as
// defined in RFC 7693. // defined in RFC 7693.
package blake2b package blake2b // import "golang.org/x/crypto/blake2b"
import ( import (
"encoding/binary" "encoding/binary"

View file

@ -54,68 +54,223 @@ DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302
DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16 GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16
// unfortunately the BYTE representation of VPERMQ must be used #define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39
#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \ #define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93
VPADDQ m0, Y0, Y0; \ #define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e
VPADDQ Y1, Y0, Y0; \ #define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93
VPXOR Y0, Y3, Y3; \ #define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39
VPSHUFD $-79, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPSHUFB c40, Y1, Y1; \
VPADDQ m1, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFB c48, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPADDQ Y1, Y1, t; \
VPSRLQ $63, Y1, Y1; \
VPXOR t, Y1, Y1; \
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39 \ // VPERMQ 0x39, Y1, Y1
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e \ // VPERMQ 0x4e, Y2, Y2
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93 \ // VPERMQ 0x93, Y3, Y3
VPADDQ m2, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFD $-79, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPSHUFB c40, Y1, Y1; \
VPADDQ m3, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFB c48, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPADDQ Y1, Y1, t; \
VPSRLQ $63, Y1, Y1; \
VPXOR t, Y1, Y1; \
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39 \ // VPERMQ 0x39, Y3, Y3
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e \ // VPERMQ 0x4e, Y2, Y2
BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93 \ // VPERMQ 0x93, Y1, Y1
// load msg into Y12, Y13, Y14, Y15 #define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \
#define LOAD_MSG_AVX2(src, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15) \ VPADDQ m0, Y0, Y0; \
MOVQ i0*8(src), X12; \ VPADDQ Y1, Y0, Y0; \
PINSRQ $1, i1*8(src), X12; \ VPXOR Y0, Y3, Y3; \
MOVQ i2*8(src), X11; \ VPSHUFD $-79, Y3, Y3; \
PINSRQ $1, i3*8(src), X11; \ VPADDQ Y3, Y2, Y2; \
VINSERTI128 $1, X11, Y12, Y12; \ VPXOR Y2, Y1, Y1; \
MOVQ i4*8(src), X13; \ VPSHUFB c40, Y1, Y1; \
PINSRQ $1, i5*8(src), X13; \ VPADDQ m1, Y0, Y0; \
MOVQ i6*8(src), X11; \ VPADDQ Y1, Y0, Y0; \
PINSRQ $1, i7*8(src), X11; \ VPXOR Y0, Y3, Y3; \
VINSERTI128 $1, X11, Y13, Y13; \ VPSHUFB c48, Y3, Y3; \
MOVQ i8*8(src), X14; \ VPADDQ Y3, Y2, Y2; \
PINSRQ $1, i9*8(src), X14; \ VPXOR Y2, Y1, Y1; \
MOVQ i10*8(src), X11; \ VPADDQ Y1, Y1, t; \
PINSRQ $1, i11*8(src), X11; \ VPSRLQ $63, Y1, Y1; \
VPXOR t, Y1, Y1; \
VPERMQ_0x39_Y1_Y1; \
VPERMQ_0x4E_Y2_Y2; \
VPERMQ_0x93_Y3_Y3; \
VPADDQ m2, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFD $-79, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPSHUFB c40, Y1, Y1; \
VPADDQ m3, Y0, Y0; \
VPADDQ Y1, Y0, Y0; \
VPXOR Y0, Y3, Y3; \
VPSHUFB c48, Y3, Y3; \
VPADDQ Y3, Y2, Y2; \
VPXOR Y2, Y1, Y1; \
VPADDQ Y1, Y1, t; \
VPSRLQ $63, Y1, Y1; \
VPXOR t, Y1, Y1; \
VPERMQ_0x39_Y3_Y3; \
VPERMQ_0x4E_Y2_Y2; \
VPERMQ_0x93_Y1_Y1
#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E
#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26
#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E
#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36
#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E
#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n
#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n
#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n
#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n
#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n
#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01
#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01
#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01
#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01
#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01
#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01
#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01
#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8
#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01
// load msg: Y12 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \
VMOVQ_SI_X12(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X12(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y12, Y12
// load msg: Y13 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \
VMOVQ_SI_X13(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X13(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y13, Y13
// load msg: Y14 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \
VMOVQ_SI_X14(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X14(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y14, Y14
// load msg: Y15 = (i0, i1, i2, i3)
// i0, i1, i2, i3 must not be 0
#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \
VMOVQ_SI_X15(i0*8); \
VMOVQ_SI_X11(i2*8); \
VPINSRQ_1_SI_X15(i1*8); \
VPINSRQ_1_SI_X11(i3*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \
VMOVQ_SI_X12_0; \
VMOVQ_SI_X11(4*8); \
VPINSRQ_1_SI_X12(2*8); \
VPINSRQ_1_SI_X11(6*8); \
VINSERTI128 $1, X11, Y12, Y12; \
LOAD_MSG_AVX2_Y13(1, 3, 5, 7); \
LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \
LOAD_MSG_AVX2_Y15(9, 11, 13, 15)
#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \
LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \
LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \
VMOVQ_SI_X11(11*8); \
VPSHUFD $0x4E, 0*8(SI), X14; \
VPINSRQ_1_SI_X11(5*8); \
VINSERTI128 $1, X11, Y14, Y14; \ VINSERTI128 $1, X11, Y14, Y14; \
MOVQ i12*8(src), X15; \ LOAD_MSG_AVX2_Y15(12, 2, 7, 3)
PINSRQ $1, i13*8(src), X15; \
MOVQ i14*8(src), X11; \ #define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \
PINSRQ $1, i15*8(src), X11; \ VMOVQ_SI_X11(5*8); \
VMOVDQU 11*8(SI), X12; \
VPINSRQ_1_SI_X11(15*8); \
VINSERTI128 $1, X11, Y12, Y12; \
VMOVQ_SI_X13(8*8); \
VMOVQ_SI_X11(2*8); \
VPINSRQ_1_SI_X13_0; \
VPINSRQ_1_SI_X11(13*8); \
VINSERTI128 $1, X11, Y13, Y13; \
LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \
LOAD_MSG_AVX2_Y15(14, 6, 1, 4)
#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \
LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \
LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \
LOAD_MSG_AVX2_Y14(2, 5, 4, 15); \
VMOVQ_SI_X15(6*8); \
VMOVQ_SI_X11_0; \
VPINSRQ_1_SI_X15(10*8); \
VPINSRQ_1_SI_X11(8*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \
LOAD_MSG_AVX2_Y12(9, 5, 2, 10); \
VMOVQ_SI_X13_0; \
VMOVQ_SI_X11(4*8); \
VPINSRQ_1_SI_X13(7*8); \
VPINSRQ_1_SI_X11(15*8); \
VINSERTI128 $1, X11, Y13, Y13; \
LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \
LOAD_MSG_AVX2_Y15(1, 12, 8, 13)
#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \
VMOVQ_SI_X12(2*8); \
VMOVQ_SI_X11_0; \
VPINSRQ_1_SI_X12(6*8); \
VPINSRQ_1_SI_X11(8*8); \
VINSERTI128 $1, X11, Y12, Y12; \
LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \
LOAD_MSG_AVX2_Y14(4, 7, 15, 1); \
LOAD_MSG_AVX2_Y15(13, 5, 14, 9)
#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \
LOAD_MSG_AVX2_Y12(12, 1, 14, 4); \
LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \
VMOVQ_SI_X14_0; \
VPSHUFD $0x4E, 8*8(SI), X11; \
VPINSRQ_1_SI_X14(6*8); \
VINSERTI128 $1, X11, Y14, Y14; \
LOAD_MSG_AVX2_Y15(7, 3, 2, 11)
#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \
LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \
LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \
LOAD_MSG_AVX2_Y14(5, 15, 8, 2); \
VMOVQ_SI_X15_0; \
VMOVQ_SI_X11(6*8); \
VPINSRQ_1_SI_X15(4*8); \
VPINSRQ_1_SI_X11(10*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \
VMOVQ_SI_X12(6*8); \
VMOVQ_SI_X11(11*8); \
VPINSRQ_1_SI_X12(14*8); \
VPINSRQ_1_SI_X11_0; \
VINSERTI128 $1, X11, Y12, Y12; \
LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \
VMOVQ_SI_X11(1*8); \
VMOVDQU 12*8(SI), X14; \
VPINSRQ_1_SI_X11(10*8); \
VINSERTI128 $1, X11, Y14, Y14; \
VMOVQ_SI_X15(2*8); \
VMOVDQU 4*8(SI), X11; \
VPINSRQ_1_SI_X15(7*8); \
VINSERTI128 $1, X11, Y15, Y15
#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \
LOAD_MSG_AVX2_Y12(10, 8, 7, 1); \
VMOVQ_SI_X13(2*8); \
VPSHUFD $0x4E, 5*8(SI), X11; \
VPINSRQ_1_SI_X13(4*8); \
VINSERTI128 $1, X11, Y13, Y13; \
LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \
VMOVQ_SI_X15(11*8); \
VMOVQ_SI_X11(12*8); \
VPINSRQ_1_SI_X15(14*8); \
VPINSRQ_1_SI_X11_0; \
VINSERTI128 $1, X11, Y15, Y15 VINSERTI128 $1, X11, Y15, Y15
// func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) // func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
@ -162,34 +317,34 @@ noinc:
VMOVDQA Y6, Y2 VMOVDQA Y6, Y2
VPXOR 0(SP), Y7, Y3 VPXOR 0(SP), Y7, Y3
LOAD_MSG_AVX2(SI, 0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15) LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15()
VMOVDQA Y12, 32(SP) VMOVDQA Y12, 32(SP)
VMOVDQA Y13, 64(SP) VMOVDQA Y13, 64(SP)
VMOVDQA Y14, 96(SP) VMOVDQA Y14, 96(SP)
VMOVDQA Y15, 128(SP) VMOVDQA Y15, 128(SP)
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3) LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3()
VMOVDQA Y12, 160(SP) VMOVDQA Y12, 160(SP)
VMOVDQA Y13, 192(SP) VMOVDQA Y13, 192(SP)
VMOVDQA Y14, 224(SP) VMOVDQA Y14, 224(SP)
VMOVDQA Y15, 256(SP) VMOVDQA Y15, 256(SP)
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4) LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8) LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13) LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9) LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11) LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10) LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5) LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
LOAD_MSG_AVX2(SI, 10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0) LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0()
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
ROUND_AVX2(32(SP), 64(SP), 96(SP), 128(SP), Y10, Y4, Y5) ROUND_AVX2(32(SP), 64(SP), 96(SP), 128(SP), Y10, Y4, Y5)
@ -209,56 +364,55 @@ noinc:
VMOVDQU Y8, 0(AX) VMOVDQU Y8, 0(AX)
VMOVDQU Y9, 32(AX) VMOVDQU Y9, 32(AX)
VZEROUPPER
MOVQ DX, SP MOVQ DX, SP
RET RET
// unfortunately the BYTE representation of VPUNPCKLQDQ and VPUNPCKHQDQ must be used #define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA
#define VPUNPCKLQDQ_X8_X8_X10 BYTE $0xC4; BYTE $0x41; BYTE $0x39; BYTE $0x6C; BYTE $0xD0 #define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB
#define VPUNPCKHQDQ_X7_X10_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF2 #define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF
#define VPUNPCKLQDQ_X7_X7_X10 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xD7 #define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD
#define VPUNPCKHQDQ_X8_X10_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x39; BYTE $0x6D; BYTE $0xFA #define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE
#define VPUNPCKLQDQ_X3_X3_X10 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xD3
#define VPUNPCKHQDQ_X2_X10_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD2 #define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7
#define VPUNPCKLQDQ_X9_X9_X10 BYTE $0xC4; BYTE $0x41; BYTE $0x31; BYTE $0x6C; BYTE $0xD1 #define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF
#define VPUNPCKHQDQ_X3_X10_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDA #define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7
#define VPUNPCKLQDQ_X2_X2_X10 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xD2 #define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF
#define VPUNPCKHQDQ_X3_X10_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD2 #define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7
#define VPUNPCKHQDQ_X8_X10_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x39; BYTE $0x6D; BYTE $0xDA #define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7
#define VPUNPCKHQDQ_X6_X10_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF2 #define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF
#define VPUNPCKHQDQ_X7_X10_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFA #define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF
// shuffle X2 and X6 using the temp registers X8, X9, X10
#define SHUFFLE_AVX() \ #define SHUFFLE_AVX() \
VMOVDQA X4, X9; \ VMOVDQA X6, X13; \
VMOVDQA X5, X4; \ VMOVDQA X2, X14; \
VMOVDQA X9, X5; \ VMOVDQA X4, X6; \
VMOVDQA X6, X8; \ VPUNPCKLQDQ_X13_X13_X15; \
VPUNPCKLQDQ_X8_X8_X10; \ VMOVDQA X5, X4; \
VPUNPCKHQDQ_X7_X10_X6; \ VMOVDQA X6, X5; \
VPUNPCKLQDQ_X7_X7_X10; \ VPUNPCKHQDQ_X15_X7_X6; \
VPUNPCKHQDQ_X8_X10_X7; \ VPUNPCKLQDQ_X7_X7_X15; \
VPUNPCKLQDQ_X3_X3_X10; \ VPUNPCKHQDQ_X15_X13_X7; \
VMOVDQA X2, X9; \ VPUNPCKLQDQ_X3_X3_X15; \
VPUNPCKHQDQ_X2_X10_X2; \ VPUNPCKHQDQ_X15_X2_X2; \
VPUNPCKLQDQ_X9_X9_X10; \ VPUNPCKLQDQ_X14_X14_X15; \
VPUNPCKHQDQ_X3_X10_X3; \ VPUNPCKHQDQ_X15_X3_X3; \
// inverse shuffle X2 and X6 using the temp registers X8, X9, X10
#define SHUFFLE_AVX_INV() \ #define SHUFFLE_AVX_INV() \
VMOVDQA X4, X9; \ VMOVDQA X2, X13; \
VMOVDQA X5, X4; \ VMOVDQA X4, X14; \
VMOVDQA X9, X5; \ VPUNPCKLQDQ_X2_X2_X15; \
VMOVDQA X2, X8; \ VMOVDQA X5, X4; \
VPUNPCKLQDQ_X2_X2_X10; \ VPUNPCKHQDQ_X15_X3_X2; \
VPUNPCKHQDQ_X3_X10_X2; \ VMOVDQA X14, X5; \
VPUNPCKLQDQ_X3_X3_X10; \ VPUNPCKLQDQ_X3_X3_X15; \
VPUNPCKHQDQ_X8_X10_X3; \ VMOVDQA X6, X14; \
VPUNPCKLQDQ_X7_X7_X10; \ VPUNPCKHQDQ_X15_X13_X3; \
VMOVDQA X6, X9; \ VPUNPCKLQDQ_X7_X7_X15; \
VPUNPCKHQDQ_X6_X10_X6; \ VPUNPCKHQDQ_X15_X6_X6; \
VPUNPCKLQDQ_X9_X9_X10; \ VPUNPCKLQDQ_X14_X14_X15; \
VPUNPCKHQDQ_X7_X10_X7; \ VPUNPCKHQDQ_X15_X7_X7; \
#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \ #define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
VPADDQ m0, v0, v0; \ VPADDQ m0, v0, v0; \
@ -294,28 +448,133 @@ noinc:
VPSRLQ $63, v3, v3; \ VPSRLQ $63, v3, v3; \
VPXOR t0, v3, v3 VPXOR t0, v3, v3
// unfortunately the BYTE representation of VPINSRQ must be used // load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7)
#define VPINSRQ_1_R10_X8_X8 BYTE $0xC4; BYTE $0x43; BYTE $0xB9; BYTE $0x22; BYTE $0xC2; BYTE $0x01 // i0, i1, i2, i3, i4, i5, i6, i7 must not be 0
#define VPINSRQ_1_R11_X9_X9 BYTE $0xC4; BYTE $0x43; BYTE $0xB1; BYTE $0x22; BYTE $0xCB; BYTE $0x01 #define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \
#define VPINSRQ_1_R12_X10_X10 BYTE $0xC4; BYTE $0x43; BYTE $0xA9; BYTE $0x22; BYTE $0xD4; BYTE $0x01 VMOVQ_SI_X12(i0*8); \
#define VPINSRQ_1_R13_X11_X11 BYTE $0xC4; BYTE $0x43; BYTE $0xA1; BYTE $0x22; BYTE $0xDD; BYTE $0x01 VMOVQ_SI_X13(i2*8); \
VMOVQ_SI_X14(i4*8); \
VMOVQ_SI_X15(i6*8); \
VPINSRQ_1_SI_X12(i1*8); \
VPINSRQ_1_SI_X13(i3*8); \
VPINSRQ_1_SI_X14(i5*8); \
VPINSRQ_1_SI_X15(i7*8)
#define VPINSRQ_1_R9_X8_X8 BYTE $0xC4; BYTE $0x43; BYTE $0xB9; BYTE $0x22; BYTE $0xC1; BYTE $0x01 // load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7)
#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \
VMOVQ_SI_X12_0; \
VMOVQ_SI_X13(4*8); \
VMOVQ_SI_X14(1*8); \
VMOVQ_SI_X15(5*8); \
VPINSRQ_1_SI_X12(2*8); \
VPINSRQ_1_SI_X13(6*8); \
VPINSRQ_1_SI_X14(3*8); \
VPINSRQ_1_SI_X15(7*8)
// load src into X8, X9, X10 and X11 using R10, R11, R12 and R13 for temp registers // load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3)
#define LOAD_MSG_AVX(src, i0, i1, i2, i3, i4, i5, i6, i7) \ #define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \
MOVQ i0*8(src), X8; \ VPSHUFD $0x4E, 0*8(SI), X12; \
MOVQ i1*8(src), R10; \ VMOVQ_SI_X13(11*8); \
MOVQ i2*8(src), X9; \ VMOVQ_SI_X14(12*8); \
MOVQ i3*8(src), R11; \ VMOVQ_SI_X15(7*8); \
MOVQ i4*8(src), X10; \ VPINSRQ_1_SI_X13(5*8); \
MOVQ i5*8(src), R12; \ VPINSRQ_1_SI_X14(2*8); \
MOVQ i6*8(src), X11; \ VPINSRQ_1_SI_X15(3*8)
MOVQ i7*8(src), R13; \
VPINSRQ_1_R10_X8_X8; \ // load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13)
VPINSRQ_1_R11_X9_X9; \ #define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \
VPINSRQ_1_R12_X10_X10; \ VMOVDQU 11*8(SI), X12; \
VPINSRQ_1_R13_X11_X11 VMOVQ_SI_X13(5*8); \
VMOVQ_SI_X14(8*8); \
VMOVQ_SI_X15(2*8); \
VPINSRQ_1_SI_X13(15*8); \
VPINSRQ_1_SI_X14_0; \
VPINSRQ_1_SI_X15(13*8)
// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8)
#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \
VMOVQ_SI_X12(2*8); \
VMOVQ_SI_X13(4*8); \
VMOVQ_SI_X14(6*8); \
VMOVQ_SI_X15_0; \
VPINSRQ_1_SI_X12(5*8); \
VPINSRQ_1_SI_X13(15*8); \
VPINSRQ_1_SI_X14(10*8); \
VPINSRQ_1_SI_X15(8*8)
// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15)
#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \
VMOVQ_SI_X12(9*8); \
VMOVQ_SI_X13(2*8); \
VMOVQ_SI_X14_0; \
VMOVQ_SI_X15(4*8); \
VPINSRQ_1_SI_X12(5*8); \
VPINSRQ_1_SI_X13(10*8); \
VPINSRQ_1_SI_X14(7*8); \
VPINSRQ_1_SI_X15(15*8)
// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3)
#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \
VMOVQ_SI_X12(2*8); \
VMOVQ_SI_X13_0; \
VMOVQ_SI_X14(12*8); \
VMOVQ_SI_X15(11*8); \
VPINSRQ_1_SI_X12(6*8); \
VPINSRQ_1_SI_X13(8*8); \
VPINSRQ_1_SI_X14(10*8); \
VPINSRQ_1_SI_X15(3*8)
// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11)
#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \
MOVQ 0*8(SI), X12; \
VPSHUFD $0x4E, 8*8(SI), X13; \
MOVQ 7*8(SI), X14; \
MOVQ 2*8(SI), X15; \
VPINSRQ_1_SI_X12(6*8); \
VPINSRQ_1_SI_X14(3*8); \
VPINSRQ_1_SI_X15(11*8)
// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8)
#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \
MOVQ 6*8(SI), X12; \
MOVQ 11*8(SI), X13; \
MOVQ 15*8(SI), X14; \
MOVQ 3*8(SI), X15; \
VPINSRQ_1_SI_X12(14*8); \
VPINSRQ_1_SI_X13_0; \
VPINSRQ_1_SI_X14(9*8); \
VPINSRQ_1_SI_X15(8*8)
// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10)
#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \
MOVQ 5*8(SI), X12; \
MOVQ 8*8(SI), X13; \
MOVQ 0*8(SI), X14; \
MOVQ 6*8(SI), X15; \
VPINSRQ_1_SI_X12(15*8); \
VPINSRQ_1_SI_X13(2*8); \
VPINSRQ_1_SI_X14(4*8); \
VPINSRQ_1_SI_X15(10*8)
// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5)
#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \
VMOVDQU 12*8(SI), X12; \
MOVQ 1*8(SI), X13; \
MOVQ 2*8(SI), X14; \
VPINSRQ_1_SI_X13(10*8); \
VPINSRQ_1_SI_X14(7*8); \
VMOVDQU 4*8(SI), X15
// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0)
#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \
MOVQ 15*8(SI), X12; \
MOVQ 3*8(SI), X13; \
MOVQ 11*8(SI), X14; \
MOVQ 12*8(SI), X15; \
VPINSRQ_1_SI_X12(9*8); \
VPINSRQ_1_SI_X13(13*8); \
VPINSRQ_1_SI_X14(14*8); \
VPINSRQ_1_SI_X15_0
// func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) // func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
@ -331,15 +590,17 @@ TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
ANDQ $~15, R9 ANDQ $~15, R9
MOVQ R9, SP MOVQ R9, SP
MOVOU ·AVX_c40<>(SB), X13 VMOVDQU ·AVX_c40<>(SB), X0
MOVOU ·AVX_c48<>(SB), X14 VMOVDQU ·AVX_c48<>(SB), X1
VMOVDQA X0, X8
VMOVDQA X1, X9
VMOVDQU ·AVX_iv3<>(SB), X0 VMOVDQU ·AVX_iv3<>(SB), X0
VMOVDQA X0, 0(SP) VMOVDQA X0, 0(SP)
XORQ CX, 0(SP) // 0(SP) = ·AVX_iv3 ^ (CX || 0) XORQ CX, 0(SP) // 0(SP) = ·AVX_iv3 ^ (CX || 0)
VMOVDQU 0(AX), X12 VMOVDQU 0(AX), X10
VMOVDQU 16(AX), X15 VMOVDQU 16(AX), X11
VMOVDQU 32(AX), X2 VMOVDQU 32(AX), X2
VMOVDQU 48(AX), X3 VMOVDQU 48(AX), X3
@ -353,124 +614,124 @@ loop:
INCQ R9 INCQ R9
noinc: noinc:
MOVQ R8, X8 VMOVQ_R8_X15
VPINSRQ_1_R9_X8_X8 VPINSRQ_1_R9_X15
VMOVDQA X12, X0 VMOVDQA X10, X0
VMOVDQA X15, X1 VMOVDQA X11, X1
VMOVDQU ·AVX_iv0<>(SB), X4 VMOVDQU ·AVX_iv0<>(SB), X4
VMOVDQU ·AVX_iv1<>(SB), X5 VMOVDQU ·AVX_iv1<>(SB), X5
VMOVDQU ·AVX_iv2<>(SB), X6 VMOVDQU ·AVX_iv2<>(SB), X6
VPXOR X8, X6, X6 VPXOR X15, X6, X6
VMOVDQA 0(SP), X7 VMOVDQA 0(SP), X7
LOAD_MSG_AVX(SI, 0, 2, 4, 6, 1, 3, 5, 7) LOAD_MSG_AVX_0_2_4_6_1_3_5_7()
VMOVDQA X8, 16(SP) VMOVDQA X12, 16(SP)
VMOVDQA X9, 32(SP) VMOVDQA X13, 32(SP)
VMOVDQA X10, 48(SP) VMOVDQA X14, 48(SP)
VMOVDQA X11, 64(SP) VMOVDQA X15, 64(SP)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 8, 10, 12, 14, 9, 11, 13, 15) LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15)
VMOVDQA X8, 80(SP) VMOVDQA X12, 80(SP)
VMOVDQA X9, 96(SP) VMOVDQA X13, 96(SP)
VMOVDQA X10, 112(SP) VMOVDQA X14, 112(SP)
VMOVDQA X11, 128(SP) VMOVDQA X15, 128(SP)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 14, 4, 9, 13, 10, 8, 15, 6) LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6)
VMOVDQA X8, 144(SP) VMOVDQA X12, 144(SP)
VMOVDQA X9, 160(SP) VMOVDQA X13, 160(SP)
VMOVDQA X10, 176(SP) VMOVDQA X14, 176(SP)
VMOVDQA X11, 192(SP) VMOVDQA X15, 192(SP)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 1, 0, 11, 5, 12, 2, 7, 3) LOAD_MSG_AVX_1_0_11_5_12_2_7_3()
VMOVDQA X8, 208(SP) VMOVDQA X12, 208(SP)
VMOVDQA X9, 224(SP) VMOVDQA X13, 224(SP)
VMOVDQA X10, 240(SP) VMOVDQA X14, 240(SP)
VMOVDQA X11, 256(SP) VMOVDQA X15, 256(SP)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 11, 12, 5, 15, 8, 0, 2, 13) LOAD_MSG_AVX_11_12_5_15_8_0_2_13()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 10, 3, 7, 9, 14, 6, 1, 4) LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 7, 3, 13, 11, 9, 1, 12, 14) LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 2, 5, 4, 15, 6, 10, 0, 8) LOAD_MSG_AVX_2_5_4_15_6_10_0_8()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 9, 5, 2, 10, 0, 7, 4, 15) LOAD_MSG_AVX_9_5_2_10_0_7_4_15()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 14, 11, 6, 3, 1, 12, 8, 13) LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 2, 6, 0, 8, 12, 10, 11, 3) LOAD_MSG_AVX_2_6_0_8_12_10_11_3()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 4, 7, 15, 1, 13, 5, 14, 9) LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 12, 1, 14, 4, 5, 15, 13, 10) LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 0, 6, 9, 8, 7, 3, 2, 11) LOAD_MSG_AVX_0_6_9_8_7_3_2_11()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 13, 7, 12, 3, 11, 14, 1, 9) LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 5, 15, 8, 2, 0, 4, 6, 10) LOAD_MSG_AVX_5_15_8_2_0_4_6_10()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 6, 14, 11, 0, 15, 9, 3, 8) LOAD_MSG_AVX_6_14_11_0_15_9_3_8()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 12, 13, 1, 10, 2, 7, 4, 5) LOAD_MSG_AVX_12_13_1_10_2_7_4_5()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
LOAD_MSG_AVX(SI, 10, 8, 7, 1, 2, 4, 6, 5) LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5)
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
LOAD_MSG_AVX(SI, 15, 9, 3, 13, 11, 14, 12, 0) LOAD_MSG_AVX_15_9_3_13_11_14_12_0()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(SP), 32(SP), 48(SP), 64(SP), X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(SP), 96(SP), 112(SP), 128(SP), X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(SP), 96(SP), 112(SP), 128(SP), X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(SP), 160(SP), 176(SP), 192(SP), X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(SP), 160(SP), 176(SP), 192(SP), X15, X8, X9)
SHUFFLE_AVX() SHUFFLE_AVX()
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(SP), 224(SP), 240(SP), 256(SP), X11, X13, X14) HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(SP), 224(SP), 240(SP), 256(SP), X15, X8, X9)
SHUFFLE_AVX_INV() SHUFFLE_AVX_INV()
VMOVDQU 32(AX), X10 VMOVDQU 32(AX), X14
VMOVDQU 48(AX), X11 VMOVDQU 48(AX), X15
VPXOR X0, X12, X12 VPXOR X0, X10, X10
VPXOR X1, X15, X15 VPXOR X1, X11, X11
VPXOR X2, X10, X10 VPXOR X2, X14, X14
VPXOR X3, X11, X11 VPXOR X3, X15, X15
VPXOR X4, X12, X12 VPXOR X4, X10, X10
VPXOR X5, X15, X15 VPXOR X5, X11, X11
VPXOR X6, X10, X2 VPXOR X6, X14, X2
VPXOR X7, X11, X3 VPXOR X7, X15, X3
VMOVDQU X2, 32(AX) VMOVDQU X2, 32(AX)
VMOVDQU X3, 48(AX) VMOVDQU X3, 48(AX)
@ -478,12 +739,11 @@ noinc:
SUBQ $128, DI SUBQ $128, DI
JNE loop JNE loop
VMOVDQU X12, 0(AX) VMOVDQU X10, 0(AX)
VMOVDQU X15, 16(AX) VMOVDQU X11, 16(AX)
MOVQ R8, 0(BX) MOVQ R8, 0(BX)
MOVQ R9, 8(BX) MOVQ R9, 8(BX)
VZEROUPPER VZEROUPPER
MOVQ BP, SP MOVQ BP, SP

View file

@ -22,7 +22,7 @@ func fromHex(s string) []byte {
func TestHashes(t *testing.T) { func TestHashes(t *testing.T) {
defer func(sse4, avx, avx2 bool) { defer func(sse4, avx, avx2 bool) {
useSSE4, useAVX, useAVX2 = sse4, useAVX, avx2 useSSE4, useAVX, useAVX2 = sse4, avx, avx2
}(useSSE4, useAVX, useAVX2) }(useSSE4, useAVX, useAVX2)
if useAVX2 { if useAVX2 {

32
vendor/golang.org/x/crypto/blake2b/register.go generated vendored Normal file
View file

@ -0,0 +1,32 @@
// Copyright 2017 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.
// +build go1.9
package blake2b
import (
"crypto"
"hash"
)
func init() {
newHash256 := func() hash.Hash {
h, _ := New256(nil)
return h
}
newHash384 := func() hash.Hash {
h, _ := New384(nil)
return h
}
newHash512 := func() hash.Hash {
h, _ := New512(nil)
return h
}
crypto.RegisterHash(crypto.BLAKE2b_256, newHash256)
crypto.RegisterHash(crypto.BLAKE2b_384, newHash384)
crypto.RegisterHash(crypto.BLAKE2b_512, newHash512)
}

View file

@ -4,7 +4,7 @@
// Package blake2s implements the BLAKE2s hash algorithm as // Package blake2s implements the BLAKE2s hash algorithm as
// defined in RFC 7693. // defined in RFC 7693.
package blake2s package blake2s // import "golang.org/x/crypto/blake2s"
import ( import (
"encoding/binary" "encoding/binary"
@ -15,8 +15,12 @@ import (
const ( const (
// The blocksize of BLAKE2s in bytes. // The blocksize of BLAKE2s in bytes.
BlockSize = 64 BlockSize = 64
// The hash size of BLAKE2s-256 in bytes. // The hash size of BLAKE2s-256 in bytes.
Size = 32 Size = 32
// The hash size of BLAKE2s-128 in bytes.
Size128 = 16
) )
var errKeySize = errors.New("blake2s: invalid key size") var errKeySize = errors.New("blake2s: invalid key size")
@ -37,6 +41,17 @@ func Sum256(data []byte) [Size]byte {
// key turns the hash into a MAC. The key must between zero and 32 bytes long. // key turns the hash into a MAC. The key must between zero and 32 bytes long.
func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) } func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
// New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
// non-empty key. Note that a 128-bit digest is too small to be secure as a
// cryptographic hash and should only be used as a MAC, thus the key argument
// is not optional.
func New128(key []byte) (hash.Hash, error) {
if len(key) == 0 {
return nil, errors.New("blake2s: a key is required for a 128-bit hash")
}
return newDigest(Size128, key)
}
func newDigest(hashSize int, key []byte) (*digest, error) { func newDigest(hashSize int, key []byte) (*digest, error) {
if len(key) > Size { if len(key) > Size {
return nil, errKeySize return nil, errKeySize

View file

@ -18,21 +18,25 @@ func TestHashes(t *testing.T) {
if useSSE4 { if useSSE4 {
t.Log("SSE4 version") t.Log("SSE4 version")
testHashes(t) testHashes(t)
testHashes128(t)
useSSE4 = false useSSE4 = false
} }
if useSSSE3 { if useSSSE3 {
t.Log("SSSE3 version") t.Log("SSSE3 version")
testHashes(t) testHashes(t)
testHashes128(t)
useSSSE3 = false useSSSE3 = false
} }
if useSSE2 { if useSSE2 {
t.Log("SSE2 version") t.Log("SSE2 version")
testHashes(t) testHashes(t)
testHashes128(t)
useSSE2 = false useSSE2 = false
} }
if useGeneric { if useGeneric {
t.Log("generic version") t.Log("generic version")
testHashes(t) testHashes(t)
testHashes128(t)
} }
} }
@ -69,6 +73,39 @@ func testHashes(t *testing.T) {
} }
} }
func testHashes128(t *testing.T) {
key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
input := make([]byte, 255)
for i := range input {
input[i] = byte(i)
}
for i, expectedHex := range hashes128 {
h, err := New128(key)
if err != nil {
t.Fatalf("#%d: error from New128: %v", i, err)
}
h.Write(input[:i])
sum := h.Sum(nil)
if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
t.Fatalf("#%d (single write): got %s, wanted %s", i, gotHex, expectedHex)
}
h.Reset()
for j := 0; j < i; j++ {
h.Write(input[j : j+1])
}
sum = h.Sum(sum[:0])
if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
t.Fatalf("#%d (byte-by-byte): got %s, wanted %s", i, gotHex, expectedHex)
}
}
}
// Benchmarks // Benchmarks
func benchmarkSum(b *testing.B, size int) { func benchmarkSum(b *testing.B, size int) {
@ -355,3 +392,262 @@ var hashes = []string{
"db444c15597b5f1a03d1f9edd16e4a9f43a667cc275175dfa2b704e3bb1a9b83", "db444c15597b5f1a03d1f9edd16e4a9f43a667cc275175dfa2b704e3bb1a9b83",
"3fb735061abc519dfe979e54c1ee5bfad0a9d858b3315bad34bde999efd724dd", "3fb735061abc519dfe979e54c1ee5bfad0a9d858b3315bad34bde999efd724dd",
} }
var hashes128 = []string{
"9536f9b267655743dee97b8a670f9f53",
"13bacfb85b48a1223c595f8c1e7e82cb",
"d47a9b1645e2feae501cd5fe44ce6333",
"1e2a79436a7796a3e9826bfedf07659f",
"7640360ed3c4f3054dba79a21dda66b7",
"d1207ac2bf5ac84fc9ef016da5a46a86",
"3123987871e59305ece3125abfc0099a",
"cf9e072ad522f2cda2d825218086731c",
"95d22870392efe2846b12b6e8e84efbb",
"7d63c30e2d51333f245601b038c0b93b",
"ed608b98e13976bdf4bedc63fa35e443",
"ed704b5cd1abf8e0dd67a6ac667a3fa5",
"77dc70109827dc74c70fd26cba379ae5",
"d2bf34508b07825ee934f33958f4560e",
"a340baa7b8a93a6e658adef42e78eeb7",
"b85c5ceaecbe9a251eac76f6932ba395",
"246519722001f6e8e97a2183f5985e53",
"5bce5aa0b7c6cac2ecf6406183cd779a",
"13408f1647c02f6efd0047ad8344f695",
"a63970f196760aa36cb965ab62f0e0fa",
"bc26f48421dd99fd45e15e736d3e7dac",
"4c6f70f9e3237cde918afb52d26f1823",
"45ed610cfbc37db80c4bf0eef14ae8d6",
"87c4c150705ea5078209ec008200539c",
"54de21f5e0e6f2afe04daeb822b6931e",
"9732a04e505064e19de3d542e7e71631",
"d2bd27e95531d6957eef511c4ba64ad4",
"7a36c9f70dcc7c3063b547101a5f6c35",
"322007d1a44c4257bc7903b183305529",
"dbcc9a09f412290ca2e0d53dfd142ddb",
"df12ed43b8e53a56db20e0f83764002c",
"d114cc11e7d5b33a360c45f18d4c7c6e",
"c43b5e836af88620a8a71b1652cb8640",
"9491c653e8867ed73c1b4ac6b5a9bb4d",
"06d0e988df94ada6c6f9f36f588ab7c5",
"561efad2480e93262c8eeaa3677615c4",
"ba8ffc702e5adc93503045eca8702312",
"5782be6ccdc78c8425285e85de8ccdc6",
"aa1c4393e4c07b53ea6e2b5b1e970771",
"42a229dc50e52271c51e8666023ebc1e",
"53706110e919f84de7f8d6c7f0e7b831",
"fc5ac8ee39cc1dd1424391323e2901bd",
"bed27b62ff66cac2fbb68193c727106a",
"cd5e689b96d0b9ea7e08dac36f7b211e",
"0b4c7f604eba058d18e322c6e1baf173",
"eb838227fdfad09a27f0f8413120675d",
"3149cf9d19a7fd529e6154a8b4c3b3ad",
"ca1e20126df930fd5fb7afe4422191e5",
"b23398f910599f3c09b6549fa81bcb46",
"27fb17c11b34fa5d8b5afe5ee3321ead",
"0f665f5f04cf2d46b7fead1a1f328158",
"8f068be73b3681f99f3b282e3c02bba5",
"ba189bbd13808dcf4e002a4dd21660d5",
"2732dcd1b16668ae6ab6a61595d0d62a",
"d410ccdd059f0e02b472ec9ec54bdd3c",
"b2eaa07b055b3a03a399971327f7e8c2",
"2e8a225655e9f99b69c60dc8b4d8e566",
"4eb55416c853f2152e67f8a224133cec",
"49552403790d8de0505a8e317a443687",
"7f2747cd41f56942752e868212c7d5ac",
"02a28f10e193b430df7112d2d98cf759",
"d4213404a9f1cf759017747cf5958270",
"faa34884344f9c65e944882db8476d34",
"ece382a8bd5018f1de5da44b72cea75b",
"f1efa90d2547036841ecd3627fafbc36",
"811ff8686d23a435ecbd0bdafcd27b1b",
"b21beea9c7385f657a76558530438721",
"9cb969da4f1b4fc5b13bf78fe366f0c4",
"8850d16d7b614d3268ccfa009d33c7fc",
"aa98a2b6176ea86415b9aff3268c6f6d",
"ec3e1efa5ed195eff667e16b1af1e39e",
"e40787dca57411d2630db2de699beb08",
"554835890735babd06318de23d31e78a",
"493957feecddc302ee2bb2086b6ebfd3",
"f6069709ad5b0139163717e9ce1114ab",
"ba5ed386098da284484b211555505a01",
"9244c8dfad8cbb68c118fa51465b3ae4",
"51e309a5008eb1f5185e5cc007cfb36f",
"6ce9ff712121b4f6087955f4911eafd4",
"59b51d8dcda031218ccdd7c760828155",
"0012878767a3d4f1c8194458cf1f8832",
"82900708afd5b6582dc16f008c655edd",
"21302c7e39b5a4cdf1d6f86b4f00c9b4",
"e894c7431591eab8d1ce0fe2aa1f01df",
"b67e1c40ee9d988226d605621854d955",
"6237bdafa34137cbbec6be43ea9bd22c",
"4172a8e19b0dcb09b978bb9eff7af52b",
"5714abb55bd4448a5a6ad09fbd872fdf",
"7ce1700bef423e1f958a94a77a94d44a",
"3742ec50cded528527775833453e0b26",
"5d41b135724c7c9c689495324b162f18",
"85c523333c6442c202e9e6e0f1185f93",
"5c71f5222d40ff5d90e7570e71ab2d30",
"6e18912e83d012efb4c66250ced6f0d9",
"4add4448c2e35e0b138a0bac7b4b1775",
"c0376c6bc5e7b8b9d2108ec25d2aab53",
"f72261d5ed156765c977751c8a13fcc1",
"cff4156c48614b6ceed3dd6b9058f17e",
"36bfb513f76c15f514bcb593419835aa",
"166bf48c6bffaf8291e6fdf63854bef4",
"0b67d33f8b859c3157fbabd9e6e47ed0",
"e4da659ca76c88e73a9f9f10f3d51789",
"33c1ae2a86b3f51c0642e6ed5b5aa1f1",
"27469b56aca2334449c1cf4970dcd969",
"b7117b2e363378aa0901b0d6a9f6ddc0",
"a9578233b09e5cd5231943fdb12cd90d",
"486d7d75253598b716a068243c1c3e89",
"66f6b02d682b78ffdc85e9ec86852489",
"38a07b9a4b228fbcc305476e4d2e05d2",
"aedb61c7970e7d05bf9002dae3c6858c",
"c03ef441f7dd30fdb61ad2d4d8e4c7da",
"7f45cc1eea9a00cb6aeb2dd748361190",
"a59538b358459132e55160899e47bd65",
"137010fef72364411820c3fbed15c8df",
"d8362b93fc504500dbd33ac74e1b4d70",
"a7e49f12c8f47e3b29cf8c0889b0a9c8",
"072e94ffbfc684bd8ab2a1b9dade2fd5",
"5ab438584bd2229e452052e002631a5f",
"f233d14221097baef57d3ec205c9e086",
"3a95db000c4a8ff98dc5c89631a7f162",
"0544f18c2994ab4ddf1728f66041ff16",
"0bc02116c60a3cc331928d6c9d3ba37e",
"b189dca6cb5b813c74200834fba97f29",
"ac8aaab075b4a5bc24419da239212650",
"1e9f19323dc71c29ae99c479dc7e8df9",
"12d944c3fa7caa1b3d62adfc492274dd",
"b4c68f1fffe8f0030e9b18aad8c9dc96",
"25887fab1422700d7fa3edc0b20206e2",
"8c09f698d03eaf88abf69f8147865ef6",
"5c363ae42a5bec26fbc5e996428d9bd7",
"7fdfc2e854fbb3928150d5e3abcf56d6",
"f0c944023f714df115f9e4f25bcdb89b",
"6d19534b4c332741c8ddd79a9644de2d",
"32595eb23764fbfc2ee7822649f74a12",
"5a51391aab33c8d575019b6e76ae052a",
"98b861ce2c620f10f913af5d704a5afd",
"b7fe2fc8b77fb1ce434f8465c7ddf793",
"0e8406e0cf8e9cc840668ece2a0fc64e",
"b89922db99c58f6a128ccffe19b6ce60",
"e1be9af665f0932b77d7f5631a511db7",
"74b96f20f58de8dc9ff5e31f91828523",
"36a4cfef5a2a7d8548db6710e50b3009",
"007e95e8d3b91948a1dedb91f75de76b",
"a87a702ce08f5745edf765bfcd5fbe0d",
"847e69a388a749a9c507354d0dddfe09",
"07176eefbc107a78f058f3d424ca6a54",
"ad7e80682333b68296f6cb2b4a8e446d",
"53c4aba43896ae422e5de5b9edbd46bf",
"33bd6c20ca2a7ab916d6e98003c6c5f8",
"060d088ea94aa093f9981a79df1dfcc8",
"5617b214b9df08d4f11e58f5e76d9a56",
"ca3a60ee85bd971e1daf9f7db059d909",
"cd2b7754505d8c884eddf736f1ec613e",
"f496163b252f1439e7e113ba2ecabd8e",
"5719c7dcf9d9f756d6213354acb7d5cf",
"6f7dd40b245c54411e7a9be83ae5701c",
"c8994dd9fdeb077a45ea04a30358b637",
"4b1184f1e35458c1c747817d527a252f",
"fc7df674afeac7a3fd994183f4c67a74",
"4f68e05ce4dcc533acf9c7c01d95711e",
"d4ebc59e918400720035dfc88e0c486a",
"d3105dd6fa123e543b0b3a6e0eeaea9e",
"874196128ed443f5bdb2800ca048fcad",
"01645f134978dc8f9cf0abc93b53780e",
"5b8b64caa257873a0ffd47c981ef6c3f",
"4ee208fc50ba0a6e65c5b58cec44c923",
"53f409a52427b3b7ffabb057ca088428",
"c1d6cd616f5341a93d921e356e5887a9",
"e85c20fea67fa7320dc23379181183c8",
"7912b6409489df001b7372bc94aebde7",
"e559f761ec866a87f1f331767fafc60f",
"20a6f5a36bc37043d977ed7708465ef8",
"6a72f526965ab120826640dd784c6cc4",
"bf486d92ad68e87c613689dd370d001b",
"d339fd0eb35edf3abd6419c8d857acaf",
"9521cd7f32306d969ddabc4e6a617f52",
"a1cd9f3e81520842f3cf6cc301cb0021",
"18e879b6f154492d593edd3f4554e237",
"66e2329c1f5137589e051592587e521e",
"e899566dd6c3e82cbc83958e69feb590",
"8a4b41d7c47e4e80659d77b4e4bfc9ae",
"f1944f6fcfc17803405a1101998c57dd",
"f6bcec07567b4f72851b307139656b18",
"22e7bb256918fe9924dce9093e2d8a27",
"dd25b925815fe7b50b7079f5f65a3970",
"0457f10f299acf0c230dd4007612e58f",
"ecb420c19efd93814fae2964d69b54af",
"14eb47b06dff685d88751c6e32789db4",
"e8f072dbb50d1ab6654aa162604a892d",
"69cff9c62092332f03a166c7b0034469",
"d3619f98970b798ca32c6c14cd25af91",
"2246d423774ee9d51a551e89c0539d9e",
"75e5d1a1e374a04a699247dad827b6cf",
"6d087dd1d4cd15bf47db07c7a96b1db8",
"967e4c055ac51b4b2a3e506cebd5826f",
"7417aa79247e473401bfa92a25b62e2a",
"24f3f4956da34b5c533d9a551ccd7b16",
"0c40382de693a5304e2331eb951cc962",
"9436f949d51b347db5c8e6258dafaaac",
"d2084297fe84c4ba6e04e4fb73d734fe",
"42a6f8ff590af21b512e9e088257aa34",
"c484ad06b1cdb3a54f3f6464a7a2a6fd",
"1b8ac860f5ceb4365400a201ed2917aa",
"c43eadabbe7b7473f3f837fc52650f54",
"0e5d3205406126b1f838875deb150d6a",
"6bf4946f8ec8a9c417f50cd1e67565be",
"42f09a2522314799c95b3fc121a0e3e8",
"06b8f1487f691a3f7c3f74e133d55870",
"1a70a65fb4f314dcf6a31451a9d2704f",
"7d4acdd0823279fd28a1e48b49a04669",
"09545cc8822a5dfc93bbab708fd69174",
"efc063db625013a83c9a426d39a9bddb",
"213bbf89b3f5be0ffdb14854bbcb2588",
"b69624d89fe2774df9a6f43695d755d4",
"c0f9ff9ded82bd73c512e365a894774d",
"d1b68507ed89c17ead6f69012982db71",
"14cf16db04648978e35c44850855d1b0",
"9f254d4eccab74cd91d694df863650a8",
"8f8946e2967baa4a814d36ff01d20813",
"6b9dc4d24ecba166cb2915d7a6cba43b",
"eb35a80418a0042b850e294db7898d4d",
"f55f925d280c637d54055c9df088ef5f",
"f48427a04f67e33f3ba0a17f7c9704a7",
"4a9f5bfcc0321aea2eced896cee65894",
"8723a67d1a1df90f1cef96e6fe81e702",
"c166c343ee25998f80bad4067960d3fd",
"dab67288d16702e676a040fd42344d73",
"c8e9e0d80841eb2c116dd14c180e006c",
"92294f546bacf0dea9042c93ecba8b34",
"013705b1502b37369ad22fe8237d444e",
"9b97f8837d5f2ebab0768fc9a6446b93",
"7e7e5236b05ec35f89edf8bf655498e7",
"7be8f2362c174c776fb9432fe93bf259",
"2422e80420276d2df5702c6470879b01",
"df645795db778bcce23bbe819a76ba48",
"3f97a4ac87dfc58761cda1782d749074",
"50e3f45df21ebfa1b706b9c0a1c245a8",
"7879541c7ff612c7ddf17cb8f7260183",
"67f6542b903b7ba1945eba1a85ee6b1c",
"b34b73d36ab6234b8d3f5494d251138e",
"0aea139641fdba59ab1103479a96e05f",
"02776815a87b8ba878453666d42afe3c",
"5929ab0a90459ebac5a16e2fb37c847e",
"c244def5b20ce0468f2b5012d04ac7fd",
"12116add6fefce36ed8a0aeccce9b6d3",
"3cd743841e9d8b878f34d91b793b4fad",
"45e87510cf5705262185f46905fae35f",
"276047016b0bfb501b2d4fc748165793",
"ddd245df5a799417d350bd7f4e0b0b7e",
"d34d917a54a2983f3fdbc4b14caae382",
"7730fbc09d0c1fb1939a8fc436f6b995",
"eb4899ef257a1711cc9270a19702e5b5",
"8a30932014bce35bba620895d374df7a",
"1924aabf9c50aa00bee5e1f95b5d9e12",
"1758d6f8b982aec9fbe50f20e3082b46",
"cd075928ab7e6883e697fe7fd3ac43ee",
}

21
vendor/golang.org/x/crypto/blake2s/register.go generated vendored Normal file
View file

@ -0,0 +1,21 @@
// Copyright 2017 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.
// +build go1.9
package blake2s
import (
"crypto"
"hash"
)
func init() {
newHash256 := func() hash.Hash {
h, _ := New256(nil)
return h
}
crypto.RegisterHash(crypto.BLAKE2s_256, newHash256)
}

View file

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539. // Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539.
package chacha20poly1305 package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305"
import ( import (
"crypto/cipher" "crypto/cipher"

View file

@ -14,13 +14,60 @@ func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
//go:noescape //go:noescape
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte) func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
//go:noescape // cpuid is implemented in chacha20poly1305_amd64.s.
func haveSSSE3() bool func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
var canUseASM bool // xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s.
func xgetbv() (eax, edx uint32)
var (
useASM bool
useAVX2 bool
)
func init() { func init() {
canUseASM = haveSSSE3() detectCPUFeatures()
}
// detectCPUFeatures is used to detect if cpu instructions
// used by the functions implemented in assembler in
// chacha20poly1305_amd64.s are supported.
func detectCPUFeatures() {
maxID, _, _, _ := cpuid(0, 0)
if maxID < 1 {
return
}
_, _, ecx1, _ := cpuid(1, 0)
haveSSSE3 := isSet(9, ecx1)
useASM = haveSSSE3
haveOSXSAVE := isSet(27, ecx1)
osSupportsAVX := false
// For XGETBV, OSXSAVE bit is required and sufficient.
if haveOSXSAVE {
eax, _ := xgetbv()
// Check if XMM and YMM registers have OS support.
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
}
haveAVX := isSet(28, ecx1) && osSupportsAVX
if maxID < 7 {
return
}
_, ebx7, _, _ := cpuid(7, 0)
haveAVX2 := isSet(5, ebx7) && haveAVX
haveBMI2 := isSet(8, ebx7)
useAVX2 = haveAVX2 && haveBMI2
}
// isSet checks if bit at bitpos is set in value.
func isSet(bitpos uint, value uint32) bool {
return value&(1<<bitpos) != 0
} }
// setupState writes a ChaCha20 input matrix to state. See // setupState writes a ChaCha20 input matrix to state. See
@ -47,7 +94,7 @@ func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
} }
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte { func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
if !canUseASM { if !useASM {
return c.sealGeneric(dst, nonce, plaintext, additionalData) return c.sealGeneric(dst, nonce, plaintext, additionalData)
} }
@ -60,7 +107,7 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []
} }
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if !canUseASM { if !useASM {
return c.openGeneric(dst, nonce, ciphertext, additionalData) return c.openGeneric(dst, nonce, ciphertext, additionalData)
} }

View file

@ -278,15 +278,8 @@ TEXT ·chacha20Poly1305Open(SB), 0, $288-97
MOVQ ad+72(FP), adp MOVQ ad+72(FP), adp
// Check for AVX2 support // Check for AVX2 support
CMPB runtime·support_avx2(SB), $0 CMPB ·useAVX2(SB), $1
JE noavx2bmi2Open JE chacha20Poly1305Open_AVX2
// Check BMI2 bit for MULXQ.
// runtime·cpuid_ebx7 is always available here
// because it passed avx2 check
TESTL $(1<<8), runtime·cpuid_ebx7(SB)
JNE chacha20Poly1305Open_AVX2
noavx2bmi2Open:
// Special optimization, for very short buffers // Special optimization, for very short buffers
CMPQ inl, $128 CMPQ inl, $128
@ -1491,16 +1484,8 @@ TEXT ·chacha20Poly1305Seal(SB), 0, $288-96
MOVQ src_len+56(FP), inl MOVQ src_len+56(FP), inl
MOVQ ad+72(FP), adp MOVQ ad+72(FP), adp
// Check for AVX2 support CMPB ·useAVX2(SB), $1
CMPB runtime·support_avx2(SB), $0 JE chacha20Poly1305Seal_AVX2
JE noavx2bmi2Seal
// Check BMI2 bit for MULXQ.
// runtime·cpuid_ebx7 is always available here
// because it passed avx2 check
TESTL $(1<<8), runtime·cpuid_ebx7(SB)
JNE chacha20Poly1305Seal_AVX2
noavx2bmi2Seal:
// Special optimization, for very short buffers // Special optimization, for very short buffers
CMPQ inl, $128 CMPQ inl, $128
@ -2709,13 +2694,21 @@ sealAVX2Tail512LoopB:
JMP sealAVX2SealHash JMP sealAVX2SealHash
// func haveSSSE3() bool // func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
TEXT ·haveSSSE3(SB), NOSPLIT, $0 TEXT ·cpuid(SB), NOSPLIT, $0-24
XORQ AX, AX MOVL eaxArg+0(FP), AX
INCL AX MOVL ecxArg+4(FP), CX
CPUID CPUID
SHRQ $9, CX MOVL AX, eax+8(FP)
ANDQ $1, CX MOVL BX, ebx+12(FP)
MOVB CX, ret+0(FP) MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET RET
// func xgetbv() (eax, edx uint32)
TEXT ·xgetbv(SB),NOSPLIT,$0-8
MOVL $0, CX
XGETBV
MOVL AX, eax+0(FP)
MOVL DX, edx+4(FP)
RET

View file

@ -1,3 +1,7 @@
// Copyright 2016 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 chacha20 package chacha20
import ( import (

604
vendor/golang.org/x/crypto/cryptobyte/asn1.go generated vendored Normal file
View file

@ -0,0 +1,604 @@
// Copyright 2017 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 cryptobyte
import (
"encoding/asn1"
"fmt"
"math/big"
"reflect"
"time"
)
// This file contains ASN.1-related methods for String and Builder.
// Tag represents an ASN.1 tag number and class (together also referred to as
// identifier octets). Methods in this package only support the low-tag-number
// form, i.e. a single identifier octet with bits 7-8 encoding the class and
// bits 1-6 encoding the tag number.
type Tag uint8
// Contructed returns t with the context-specific class bit set.
func (t Tag) ContextSpecific() Tag { return t | 0x80 }
// Contructed returns t with the constructed class bit set.
func (t Tag) Constructed() Tag { return t | 0x20 }
// Builder
// AddASN1Int64 appends a DER-encoded ASN.1 INTEGER.
func (b *Builder) AddASN1Int64(v int64) {
b.addASN1Signed(asn1.TagInteger, v)
}
// AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION.
func (b *Builder) AddASN1Enum(v int64) {
b.addASN1Signed(asn1.TagEnum, v)
}
func (b *Builder) addASN1Signed(tag Tag, v int64) {
b.AddASN1(tag, func(c *Builder) {
length := 1
for i := v; i >= 0x80 || i < -0x80; i >>= 8 {
length++
}
for ; length > 0; length-- {
i := v >> uint((length-1)*8) & 0xff
c.AddUint8(uint8(i))
}
})
}
// AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER.
func (b *Builder) AddASN1Uint64(v uint64) {
b.AddASN1(asn1.TagInteger, func(c *Builder) {
length := 1
for i := v; i >= 0x80; i >>= 8 {
length++
}
for ; length > 0; length-- {
i := v >> uint((length-1)*8) & 0xff
c.AddUint8(uint8(i))
}
})
}
// AddASN1BigInt appends a DER-encoded ASN.1 INTEGER.
func (b *Builder) AddASN1BigInt(n *big.Int) {
if b.err != nil {
return
}
b.AddASN1(asn1.TagInteger, func(c *Builder) {
if n.Sign() < 0 {
// A negative number has to be converted to two's-complement form. So we
// invert and subtract 1. If the most-significant-bit isn't set then
// we'll need to pad the beginning with 0xff in order to keep the number
// negative.
nMinus1 := new(big.Int).Neg(n)
nMinus1.Sub(nMinus1, bigOne)
bytes := nMinus1.Bytes()
for i := range bytes {
bytes[i] ^= 0xff
}
if bytes[0]&0x80 == 0 {
c.add(0xff)
}
c.add(bytes...)
} else if n.Sign() == 0 {
c.add(0)
} else {
bytes := n.Bytes()
if bytes[0]&0x80 != 0 {
c.add(0)
}
c.add(bytes...)
}
})
}
// AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING.
func (b *Builder) AddASN1OctetString(bytes []byte) {
b.AddASN1(asn1.TagOctetString, func(c *Builder) {
c.AddBytes(bytes)
})
}
const generalizedTimeFormatStr = "20060102150405Z0700"
// AddASN1GeneralizedTime appends a DER-encoded ASN.1 GENERALIZEDTIME.
func (b *Builder) AddASN1GeneralizedTime(t time.Time) {
if t.Year() < 0 || t.Year() > 9999 {
b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t)
return
}
b.AddASN1(asn1.TagGeneralizedTime, func(c *Builder) {
c.AddBytes([]byte(t.Format(generalizedTimeFormatStr)))
})
}
// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING.
func (b *Builder) AddASN1BitString(s asn1.BitString) {
// TODO(martinkr): Implement.
b.MarshalASN1(s)
}
// MarshalASN1 calls asn1.Marshal on its input and appends the result if
// successful or records an error if one occurred.
func (b *Builder) MarshalASN1(v interface{}) {
// NOTE(martinkr): This is somewhat of a hack to allow propagation of
// asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a
// value embedded into a struct, its tag information is lost.
if b.err != nil {
return
}
bytes, err := asn1.Marshal(v)
if err != nil {
b.err = err
return
}
b.AddBytes(bytes)
}
// AddASN1 appends an ASN.1 object. The object is prefixed with the given tag.
// Tags greater than 30 are not supported and result in an error (i.e.
// low-tag-number form only). The child builder passed to the
// BuilderContinuation can be used to build the content of the ASN.1 object.
func (b *Builder) AddASN1(tag Tag, f BuilderContinuation) {
if b.err != nil {
return
}
// Identifiers with the low five bits set indicate high-tag-number format
// (two or more octets), which we don't support.
if tag&0x1f == 0x1f {
b.err = fmt.Errorf("cryptobyte: high-tag number identifier octects not supported: 0x%x", tag)
return
}
b.AddUint8(uint8(tag))
b.addLengthPrefixed(1, true, f)
}
// String
var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
// not point to an integer or to a big.Int, it panics. It returns true on
// success and false on error.
func (s *String) ReadASN1Integer(out interface{}) bool {
if reflect.TypeOf(out).Kind() != reflect.Ptr {
panic("out is not a pointer")
}
switch reflect.ValueOf(out).Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var i int64
if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) {
return false
}
reflect.ValueOf(out).Elem().SetInt(i)
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
var u uint64
if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) {
return false
}
reflect.ValueOf(out).Elem().SetUint(u)
return true
case reflect.Struct:
if reflect.TypeOf(out).Elem() == bigIntType {
return s.readASN1BigInt(out.(*big.Int))
}
}
panic("out does not point to an integer type")
}
func checkASN1Integer(bytes []byte) bool {
if len(bytes) == 0 {
// An INTEGER is encoded with at least one octet.
return false
}
if len(bytes) == 1 {
return true
}
if bytes[0] == 0 && bytes[1]&0x80 == 0 || bytes[0] == 0xff && bytes[1]&0x80 == 0x80 {
// Value is not minimally encoded.
return false
}
return true
}
var bigOne = big.NewInt(1)
func (s *String) readASN1BigInt(out *big.Int) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) {
return false
}
if bytes[0]&0x80 == 0x80 {
// Negative number.
neg := make([]byte, len(bytes))
for i, b := range bytes {
neg[i] = ^b
}
out.SetBytes(neg)
out.Add(out, bigOne)
out.Neg(out)
} else {
out.SetBytes(bytes)
}
return true
}
func (s *String) readASN1Int64(out *int64) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
return false
}
return true
}
func asn1Signed(out *int64, n []byte) bool {
length := len(n)
if length > 8 {
return false
}
for i := 0; i < length; i++ {
*out <<= 8
*out |= int64(n[i])
}
// Shift up and down in order to sign extend the result.
*out <<= 64 - uint8(length)*8
*out >>= 64 - uint8(length)*8
return true
}
func (s *String) readASN1Uint64(out *uint64) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.TagInteger) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) {
return false
}
return true
}
func asn1Unsigned(out *uint64, n []byte) bool {
length := len(n)
if length > 9 || length == 9 && n[0] != 0 {
// Too large for uint64.
return false
}
if n[0]&0x80 != 0 {
// Negative number.
return false
}
for i := 0; i < length; i++ {
*out <<= 8
*out |= uint64(n[i])
}
return true
}
// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It returns
// true on success and false on error.
func (s *String) ReadASN1Enum(out *int) bool {
var bytes String
var i int64
if !s.ReadASN1(&bytes, asn1.TagEnum) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) {
return false
}
if int64(int(i)) != i {
return false
}
*out = int(i)
return true
}
func (s *String) readBase128Int(out *int) bool {
ret := 0
for i := 0; len(*s) > 0; i++ {
if i == 4 {
return false
}
ret <<= 7
b := s.read(1)[0]
ret |= int(b & 0x7f)
if b&0x80 == 0 {
*out = ret
return true
}
}
return false // truncated
}
// ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and
// advances. It returns true on success and false on error.
func (s *String) ReadASN1ObjectIdentifier(out *asn1.ObjectIdentifier) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.TagOID) || len(bytes) == 0 {
return false
}
// In the worst case, we get two elements from the first byte (which is
// encoded differently) and then every varint is a single byte long.
components := make([]int, len(bytes)+1)
// The first varint is 40*value1 + value2:
// According to this packing, value1 can take the values 0, 1 and 2 only.
// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
// then there are no restrictions on value2.
var v int
if !bytes.readBase128Int(&v) {
return false
}
if v < 80 {
components[0] = v / 40
components[1] = v % 40
} else {
components[0] = 2
components[1] = v - 80
}
i := 2
for ; len(bytes) > 0; i++ {
if !bytes.readBase128Int(&v) {
return false
}
components[i] = v
}
*out = components[:i]
return true
}
// ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and
// advances. It returns true on success and false on error.
func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.TagGeneralizedTime) {
return false
}
t := string(bytes)
res, err := time.Parse(generalizedTimeFormatStr, t)
if err != nil {
return false
}
if serialized := res.Format(generalizedTimeFormatStr); serialized != t {
return false
}
*out = res
return true
}
// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. It
// returns true on success and false on error.
func (s *String) ReadASN1BitString(out *asn1.BitString) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.TagBitString) || len(bytes) == 0 {
return false
}
paddingBits := uint8(bytes[0])
bytes = bytes[1:]
if paddingBits > 7 ||
len(bytes) == 0 && paddingBits != 0 ||
len(bytes) > 0 && bytes[len(bytes)-1]&(1<<paddingBits-1) != 0 {
return false
}
out.BitLength = len(bytes)*8 - int(paddingBits)
out.Bytes = bytes
return true
}
// ReadASN1Bytes reads the contents of a DER-encoded ASN.1 element (not including
// tag and length bytes) into out, and advances. The element must match the
// given tag. It returns true on success and false on error.
func (s *String) ReadASN1Bytes(out *[]byte, tag Tag) bool {
return s.ReadASN1((*String)(out), tag)
}
// ReadASN1 reads the contents of a DER-encoded ASN.1 element (not including
// tag and length bytes) into out, and advances. The element must match the
// given tag. It returns true on success and false on error.
//
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
func (s *String) ReadASN1(out *String, tag Tag) bool {
var t Tag
if !s.ReadAnyASN1(out, &t) || t != tag {
return false
}
return true
}
// ReadASN1Element reads the contents of a DER-encoded ASN.1 element (including
// tag and length bytes) into out, and advances. The element must match the
// given tag. It returns true on success and false on error.
//
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
func (s *String) ReadASN1Element(out *String, tag Tag) bool {
var t Tag
if !s.ReadAnyASN1Element(out, &t) || t != tag {
return false
}
return true
}
// ReadAnyASN1 reads the contents of a DER-encoded ASN.1 element (not including
// tag and length bytes) into out, sets outTag to its tag, and advances. It
// returns true on success and false on error.
//
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
func (s *String) ReadAnyASN1(out *String, outTag *Tag) bool {
return s.readASN1(out, outTag, true /* skip header */)
}
// ReadAnyASN1Element reads the contents of a DER-encoded ASN.1 element
// (including tag and length bytes) into out, sets outTag to is tag, and
// advances. It returns true on success and false on error.
//
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
func (s *String) ReadAnyASN1Element(out *String, outTag *Tag) bool {
return s.readASN1(out, outTag, false /* include header */)
}
// PeekASN1Tag returns true if the next ASN.1 value on the string starts with
// the given tag.
func (s String) PeekASN1Tag(tag Tag) bool {
if len(s) == 0 {
return false
}
return Tag(s[0]) == tag
}
// ReadOptionalASN1 attempts to read the contents of a DER-encoded ASN.Element
// (not including tag and length bytes) tagged with the given tag into out. It
// stores whether an element with the tag was found in outPresent, unless
// outPresent is nil. It returns true on success and false on error.
func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag Tag) bool {
present := s.PeekASN1Tag(tag)
if outPresent != nil {
*outPresent = present
}
if present && !s.ReadASN1(out, tag) {
return false
}
return true
}
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER
// explicitly tagged with tag into out and advances. If no element with a
// matching tag is present, it writes defaultValue into out instead. If out
// does not point to an integer or to a big.Int, it panics. It returns true on
// success and false on error.
func (s *String) ReadOptionalASN1Integer(out interface{}, tag Tag, defaultValue interface{}) bool {
if reflect.TypeOf(out).Kind() != reflect.Ptr {
panic("out is not a pointer")
}
var present bool
var i String
if !s.ReadOptionalASN1(&i, &present, tag) {
return false
}
if !present {
switch reflect.ValueOf(out).Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue))
case reflect.Struct:
if reflect.TypeOf(out).Elem() != bigIntType {
panic("invalid integer type")
}
if reflect.TypeOf(defaultValue).Kind() != reflect.Ptr ||
reflect.TypeOf(defaultValue).Elem() != bigIntType {
panic("out points to big.Int, but defaultValue does not")
}
out.(*big.Int).Set(defaultValue.(*big.Int))
default:
panic("invalid integer type")
}
return true
}
if !i.ReadASN1Integer(out) || !i.Empty() {
return false
}
return true
}
// ReadOptionalASN1OctetString attempts to read an optional ASN.1 OCTET STRING
// explicitly tagged with tag into out and advances. If no element with a
// matching tag is present, it writes defaultValue into out instead. It returns
// true on success and false on error.
func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag Tag) bool {
var present bool
var child String
if !s.ReadOptionalASN1(&child, &present, tag) {
return false
}
if outPresent != nil {
*outPresent = present
}
if present {
var oct String
if !child.ReadASN1(&oct, asn1.TagOctetString) || !child.Empty() {
return false
}
*out = oct
} else {
*out = nil
}
return true
}
func (s *String) readASN1(out *String, outTag *Tag, skipHeader bool) bool {
if len(*s) < 2 {
return false
}
tag, lenByte := (*s)[0], (*s)[1]
if tag&0x1f == 0x1f {
// ITU-T X.690 section 8.1.2
//
// An identifier octet with a tag part of 0x1f indicates a high-tag-number
// form identifier with two or more octets. We only support tags less than
// 31 (i.e. low-tag-number form, single octet identifier).
return false
}
if outTag != nil {
*outTag = Tag(tag)
}
// ITU-T X.690 section 8.1.3
//
// Bit 8 of the first length byte indicates whether the length is short- or
// long-form.
var length, headerLen uint32 // length includes headerLen
if lenByte&0x80 == 0 {
// Short-form length (section 8.1.3.4), encoded in bits 1-7.
length = uint32(lenByte) + 2
headerLen = 2
} else {
// Long-form length (section 8.1.3.5). Bits 1-7 encode the number of octets
// used to encode the length.
lenLen := lenByte & 0x7f
var len32 uint32
if lenLen == 0 || lenLen > 4 || len(*s) < int(2+lenLen) {
return false
}
lenBytes := String((*s)[2 : 2+lenLen])
if !lenBytes.readUnsigned(&len32, int(lenLen)) {
return false
}
// ITU-T X.690 section 10.1 (DER length forms) requires encoding the length
// with the minimum number of octets.
if len32 < 128 {
// Length should have used short-form encoding.
return false
}
if len32>>((lenLen-1)*8) == 0 {
// Leading octet is 0. Length should have been at least one byte shorter.
return false
}
headerLen = 2 + uint32(lenLen)
if headerLen+len32 < len32 {
// Overflow.
return false
}
length = headerLen + len32
}
if uint32(int(length)) != length || !s.ReadBytes((*[]byte)(out), int(length)) {
return false
}
if skipHeader && !out.Skip(int(headerLen)) {
panic("cryptobyte: internal error")
}
return true
}

285
vendor/golang.org/x/crypto/cryptobyte/asn1_test.go generated vendored Normal file
View file

@ -0,0 +1,285 @@
// Copyright 2017 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 cryptobyte
import (
"bytes"
"encoding/asn1"
"math/big"
"reflect"
"testing"
"time"
)
type readASN1Test struct {
name string
in []byte
tag Tag
ok bool
out interface{}
}
var readASN1TestData = []readASN1Test{
{"valid", []byte{0x30, 2, 1, 2}, 0x30, true, []byte{1, 2}},
{"truncated", []byte{0x30, 3, 1, 2}, 0x30, false, nil},
{"zero length of length", []byte{0x30, 0x80}, 0x30, false, nil},
{"invalid long form length", []byte{0x30, 0x81, 1, 1}, 0x30, false, nil},
{"non-minimal length", append([]byte{0x30, 0x82, 0, 0x80}, make([]byte, 0x80)...), 0x30, false, nil},
{"invalid tag", []byte{0xa1, 3, 0x4, 1, 1}, 31, false, nil},
{"high tag", []byte{0x1f, 0x81, 0x80, 0x01, 2, 1, 2}, 0xff /* actually 0x4001, but tag is uint8 */, false, nil},
}
func TestReadASN1(t *testing.T) {
for _, test := range readASN1TestData {
t.Run(test.name, func(t *testing.T) {
var in, out String = test.in, nil
ok := in.ReadASN1(&out, test.tag)
if ok != test.ok || ok && !bytes.Equal(out, test.out.([]byte)) {
t.Errorf("in.ReadASN1() = %v, want %v; out = %v, want %v", ok, test.ok, out, test.out)
}
})
}
}
func TestReadASN1Optional(t *testing.T) {
var empty String
var present bool
ok := empty.ReadOptionalASN1(nil, &present, 0xa0)
if !ok || present {
t.Errorf("empty.ReadOptionalASN1() = %v, want true; present = %v want false", ok, present)
}
var in, out String = []byte{0xa1, 3, 0x4, 1, 1}, nil
ok = in.ReadOptionalASN1(&out, &present, 0xa0)
if !ok || present {
t.Errorf("in.ReadOptionalASN1() = %v, want true, present = %v, want false", ok, present)
}
ok = in.ReadOptionalASN1(&out, &present, 0xa1)
wantBytes := []byte{4, 1, 1}
if !ok || !present || !bytes.Equal(out, wantBytes) {
t.Errorf("in.ReadOptionalASN1() = %v, want true; present = %v, want true; out = %v, want = %v", ok, present, out, wantBytes)
}
}
var optionalOctetStringTestData = []struct {
readASN1Test
present bool
}{
{readASN1Test{"empty", []byte{}, 0xa0, true, []byte{}}, false},
{readASN1Test{"invalid", []byte{0xa1, 3, 0x4, 2, 1}, 0xa1, false, []byte{}}, true},
{readASN1Test{"missing", []byte{0xa1, 3, 0x4, 1, 1}, 0xa0, true, []byte{}}, false},
{readASN1Test{"present", []byte{0xa1, 3, 0x4, 1, 1}, 0xa1, true, []byte{1}}, true},
}
func TestReadASN1OptionalOctetString(t *testing.T) {
for _, test := range optionalOctetStringTestData {
t.Run(test.name, func(t *testing.T) {
in := String(test.in)
var out []byte
var present bool
ok := in.ReadOptionalASN1OctetString(&out, &present, test.tag)
if ok != test.ok || present != test.present || !bytes.Equal(out, test.out.([]byte)) {
t.Errorf("in.ReadOptionalASN1OctetString() = %v, want %v; present = %v want %v; out = %v, want %v", ok, test.ok, present, test.present, out, test.out)
}
})
}
}
const defaultInt = -1
var optionalIntTestData = []readASN1Test{
{"empty", []byte{}, 0xa0, true, defaultInt},
{"invalid", []byte{0xa1, 3, 0x2, 2, 127}, 0xa1, false, 0},
{"missing", []byte{0xa1, 3, 0x2, 1, 127}, 0xa0, true, defaultInt},
{"present", []byte{0xa1, 3, 0x2, 1, 42}, 0xa1, true, 42},
}
func TestReadASN1OptionalInteger(t *testing.T) {
for _, test := range optionalIntTestData {
t.Run(test.name, func(t *testing.T) {
in := String(test.in)
var out int
ok := in.ReadOptionalASN1Integer(&out, test.tag, defaultInt)
if ok != test.ok || ok && out != test.out.(int) {
t.Errorf("in.ReadOptionalASN1Integer() = %v, want %v; out = %v, want %v", ok, test.ok, out, test.out)
}
})
}
}
func TestReadASN1IntegerSigned(t *testing.T) {
testData64 := []struct {
in []byte
out int64
}{
{[]byte{2, 3, 128, 0, 0}, -0x800000},
{[]byte{2, 2, 255, 0}, -256},
{[]byte{2, 2, 255, 127}, -129},
{[]byte{2, 1, 128}, -128},
{[]byte{2, 1, 255}, -1},
{[]byte{2, 1, 0}, 0},
{[]byte{2, 1, 1}, 1},
{[]byte{2, 1, 2}, 2},
{[]byte{2, 1, 127}, 127},
{[]byte{2, 2, 0, 128}, 128},
{[]byte{2, 2, 1, 0}, 256},
{[]byte{2, 4, 0, 128, 0, 0}, 0x800000},
}
for i, test := range testData64 {
in := String(test.in)
var out int64
ok := in.ReadASN1Integer(&out)
if !ok || out != test.out {
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %d, want %d", i, ok, out, test.out)
}
}
// Repeat the same cases, reading into a big.Int.
t.Run("big.Int", func(t *testing.T) {
for i, test := range testData64 {
in := String(test.in)
var out big.Int
ok := in.ReadASN1Integer(&out)
if !ok || out.Int64() != test.out {
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %d, want %d", i, ok, out.Int64(), test.out)
}
}
})
}
func TestReadASN1IntegerUnsigned(t *testing.T) {
testData := []struct {
in []byte
out uint64
}{
{[]byte{2, 1, 0}, 0},
{[]byte{2, 1, 1}, 1},
{[]byte{2, 1, 2}, 2},
{[]byte{2, 1, 127}, 127},
{[]byte{2, 2, 0, 128}, 128},
{[]byte{2, 2, 1, 0}, 256},
{[]byte{2, 4, 0, 128, 0, 0}, 0x800000},
{[]byte{2, 8, 127, 255, 255, 255, 255, 255, 255, 255}, 0x7fffffffffffffff},
{[]byte{2, 9, 0, 128, 0, 0, 0, 0, 0, 0, 0}, 0x8000000000000000},
{[]byte{2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255}, 0xffffffffffffffff},
}
for i, test := range testData {
in := String(test.in)
var out uint64
ok := in.ReadASN1Integer(&out)
if !ok || out != test.out {
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %d, want %d", i, ok, out, test.out)
}
}
}
func TestReadASN1IntegerInvalid(t *testing.T) {
testData := []String{
[]byte{3, 1, 0}, // invalid tag
// truncated
[]byte{2, 1},
[]byte{2, 2, 0},
// not minimally encoded
[]byte{2, 2, 0, 1},
[]byte{2, 2, 0xff, 0xff},
}
for i, test := range testData {
var out int64
if test.ReadASN1Integer(&out) {
t.Errorf("#%d: in.ReadASN1Integer() = true, want false (out = %d)", i, out)
}
}
}
func TestReadASN1ObjectIdentifier(t *testing.T) {
testData := []struct {
in []byte
ok bool
out []int
}{
{[]byte{}, false, []int{}},
{[]byte{6, 0}, false, []int{}},
{[]byte{5, 1, 85}, false, []int{2, 5}},
{[]byte{6, 1, 85}, true, []int{2, 5}},
{[]byte{6, 2, 85, 0x02}, true, []int{2, 5, 2}},
{[]byte{6, 4, 85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}},
{[]byte{6, 3, 0x81, 0x34, 0x03}, true, []int{2, 100, 3}},
{[]byte{6, 7, 85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}},
}
for i, test := range testData {
in := String(test.in)
var out asn1.ObjectIdentifier
ok := in.ReadASN1ObjectIdentifier(&out)
if ok != test.ok || ok && !out.Equal(test.out) {
t.Errorf("#%d: in.ReadASN1ObjectIdentifier() = %v, want %v; out = %v, want %v", i, ok, test.ok, out, test.out)
}
}
}
func TestReadASN1GeneralizedTime(t *testing.T) {
testData := []struct {
in string
ok bool
out time.Time
}{
{"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)},
{"20100102030405", false, time.Time{}},
{"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))},
{"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))},
/* These are invalid times. However, the time package normalises times
* and they were accepted in some versions. See #11134. */
{"00000100000000Z", false, time.Time{}},
{"20101302030405Z", false, time.Time{}},
{"20100002030405Z", false, time.Time{}},
{"20100100030405Z", false, time.Time{}},
{"20100132030405Z", false, time.Time{}},
{"20100231030405Z", false, time.Time{}},
{"20100102240405Z", false, time.Time{}},
{"20100102036005Z", false, time.Time{}},
{"20100102030460Z", false, time.Time{}},
{"-20100102030410Z", false, time.Time{}},
{"2010-0102030410Z", false, time.Time{}},
{"2010-0002030410Z", false, time.Time{}},
{"201001-02030410Z", false, time.Time{}},
{"20100102-030410Z", false, time.Time{}},
{"2010010203-0410Z", false, time.Time{}},
{"201001020304-10Z", false, time.Time{}},
}
for i, test := range testData {
in := String(append([]byte{asn1.TagGeneralizedTime, byte(len(test.in))}, test.in...))
var out time.Time
ok := in.ReadASN1GeneralizedTime(&out)
if ok != test.ok || ok && !reflect.DeepEqual(out, test.out) {
t.Errorf("#%d: in.ReadASN1GeneralizedTime() = %v, want %v; out = %q, want %q", i, ok, test.ok, out, test.out)
}
}
}
func TestReadASN1BitString(t *testing.T) {
testData := []struct {
in []byte
ok bool
out asn1.BitString
}{
{[]byte{}, false, asn1.BitString{}},
{[]byte{0x00}, true, asn1.BitString{}},
{[]byte{0x07, 0x00}, true, asn1.BitString{Bytes: []byte{0}, BitLength: 1}},
{[]byte{0x07, 0x01}, false, asn1.BitString{}},
{[]byte{0x07, 0x40}, false, asn1.BitString{}},
{[]byte{0x08, 0x00}, false, asn1.BitString{}},
{[]byte{0xff}, false, asn1.BitString{}},
{[]byte{0xfe, 0x00}, false, asn1.BitString{}},
}
for i, test := range testData {
in := String(append([]byte{3, byte(len(test.in))}, test.in...))
var out asn1.BitString
ok := in.ReadASN1BitString(&out)
if ok != test.ok || ok && (!bytes.Equal(out.Bytes, test.out.Bytes) || out.BitLength != test.out.BitLength) {
t.Errorf("#%d: in.ReadASN1BitString() = %v, want %v; out = %v, want %v", i, ok, test.ok, out, test.out)
}
}
}

255
vendor/golang.org/x/crypto/cryptobyte/builder.go generated vendored Normal file
View file

@ -0,0 +1,255 @@
// Copyright 2017 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 cryptobyte
import (
"errors"
"fmt"
)
// A Builder builds byte strings from fixed-length and length-prefixed values.
// The zero value is a usable Builder that allocates space as needed.
type Builder struct {
err error
result []byte
fixedSize bool
child *Builder
offset int
pendingLenLen int
pendingIsASN1 bool
}
// NewBuilder creates a Builder that appends its output to the given buffer.
// Like append(), the slice will be reallocated if its capacity is exceeded.
// Use Bytes to get the final buffer.
func NewBuilder(buffer []byte) *Builder {
return &Builder{
result: buffer,
}
}
// NewFixedBuilder creates a Builder that appends its output into the given
// buffer. This builder does not reallocate the output buffer. Writes that
// would exceed the buffer's capacity are treated as an error.
func NewFixedBuilder(buffer []byte) *Builder {
return &Builder{
result: buffer,
fixedSize: true,
}
}
// Bytes returns the bytes written by the builder or an error if one has
// occurred during during building.
func (b *Builder) Bytes() ([]byte, error) {
if b.err != nil {
return nil, b.err
}
return b.result[b.offset:], nil
}
// BytesOrPanic returns the bytes written by the builder or panics if an error
// has occurred during building.
func (b *Builder) BytesOrPanic() []byte {
if b.err != nil {
panic(b.err)
}
return b.result[b.offset:]
}
// AddUint8 appends an 8-bit value to the byte string.
func (b *Builder) AddUint8(v uint8) {
b.add(byte(v))
}
// AddUint16 appends a big-endian, 16-bit value to the byte string.
func (b *Builder) AddUint16(v uint16) {
b.add(byte(v>>8), byte(v))
}
// AddUint24 appends a big-endian, 24-bit value to the byte string. The highest
// byte of the 32-bit input value is silently truncated.
func (b *Builder) AddUint24(v uint32) {
b.add(byte(v>>16), byte(v>>8), byte(v))
}
// AddUint32 appends a big-endian, 32-bit value to the byte string.
func (b *Builder) AddUint32(v uint32) {
b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
// AddBytes appends a sequence of bytes to the byte string.
func (b *Builder) AddBytes(v []byte) {
b.add(v...)
}
// BuilderContinuation is continuation-passing interface for building
// length-prefixed byte sequences. Builder methods for length-prefixed
// sequences (AddUint8LengthPrefixed etc.) will invoke the BuilderContinuation
// supplied to them. The child builder passed to the continuation can be used
// to build the content of the length-prefixed sequence. Example:
//
// parent := cryptobyte.NewBuilder()
// parent.AddUint8LengthPrefixed(func (child *Builder) {
// child.AddUint8(42)
// child.AddUint8LengthPrefixed(func (grandchild *Builder) {
// grandchild.AddUint8(5)
// })
// })
//
// It is an error to write more bytes to the child than allowed by the reserved
// length prefix. After the continuation returns, the child must be considered
// invalid, i.e. users must not store any copies or references of the child
// that outlive the continuation.
type BuilderContinuation func(child *Builder)
// AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence.
func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) {
b.addLengthPrefixed(1, false, f)
}
// AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence.
func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) {
b.addLengthPrefixed(2, false, f)
}
// AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence.
func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) {
b.addLengthPrefixed(3, false, f)
}
func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) {
// Subsequent writes can be ignored if the builder has encountered an error.
if b.err != nil {
return
}
offset := len(b.result)
b.add(make([]byte, lenLen)...)
b.child = &Builder{
result: b.result,
fixedSize: b.fixedSize,
offset: offset,
pendingLenLen: lenLen,
pendingIsASN1: isASN1,
}
f(b.child)
b.flushChild()
if b.child != nil {
panic("cryptobyte: internal error")
}
}
func (b *Builder) flushChild() {
if b.child == nil {
return
}
b.child.flushChild()
child := b.child
b.child = nil
if child.err != nil {
b.err = child.err
return
}
length := len(child.result) - child.pendingLenLen - child.offset
if length < 0 {
panic("cryptobyte: internal error") // result unexpectedly shrunk
}
if child.pendingIsASN1 {
// For ASN.1, we reserved a single byte for the length. If that turned out
// to be incorrect, we have to move the contents along in order to make
// space.
if child.pendingLenLen != 1 {
panic("cryptobyte: internal error")
}
var lenLen, lenByte uint8
if int64(length) > 0xfffffffe {
b.err = errors.New("pending ASN.1 child too long")
return
} else if length > 0xffffff {
lenLen = 5
lenByte = 0x80 | 4
} else if length > 0xffff {
lenLen = 4
lenByte = 0x80 | 3
} else if length > 0xff {
lenLen = 3
lenByte = 0x80 | 2
} else if length > 0x7f {
lenLen = 2
lenByte = 0x80 | 1
} else {
lenLen = 1
lenByte = uint8(length)
length = 0
}
// Insert the initial length byte, make space for successive length bytes,
// and adjust the offset.
child.result[child.offset] = lenByte
extraBytes := int(lenLen - 1)
if extraBytes != 0 {
child.add(make([]byte, extraBytes)...)
childStart := child.offset + child.pendingLenLen
copy(child.result[childStart+extraBytes:], child.result[childStart:])
}
child.offset++
child.pendingLenLen = extraBytes
}
l := length
for i := child.pendingLenLen - 1; i >= 0; i-- {
child.result[child.offset+i] = uint8(l)
l >>= 8
}
if l != 0 {
b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen)
return
}
if !b.fixedSize {
b.result = child.result // In case child reallocated result.
}
}
func (b *Builder) add(bytes ...byte) {
if b.err != nil {
return
}
if b.child != nil {
panic("attempted write while child is pending")
}
if len(b.result)+len(bytes) < len(bytes) {
b.err = errors.New("cryptobyte: length overflow")
}
if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) {
b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer")
return
}
b.result = append(b.result, bytes...)
}
// A MarshalingValue marshals itself into a Builder.
type MarshalingValue interface {
// Marshal is called by Builder.AddValue. It receives a pointer to a builder
// to marshal itself into. It may return an error that occurred during
// marshaling, such as unset or invalid values.
Marshal(b *Builder) error
}
// AddValue calls Marshal on v, passing a pointer to the builder to append to.
// If Marshal returns an error, it is set on the Builder so that subsequent
// appends don't have an effect.
func (b *Builder) AddValue(v MarshalingValue) {
err := v.Marshal(b)
if err != nil {
b.err = err
}
}

View file

@ -0,0 +1,379 @@
// Copyright 2017 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 cryptobyte
import (
"bytes"
"fmt"
"testing"
)
func builderBytesEq(b *Builder, want ...byte) error {
got := b.BytesOrPanic()
if !bytes.Equal(got, want) {
return fmt.Errorf("Bytes() = %v, want %v", got, want)
}
return nil
}
func TestBytes(t *testing.T) {
var b Builder
v := []byte("foobarbaz")
b.AddBytes(v[0:3])
b.AddBytes(v[3:4])
b.AddBytes(v[4:9])
if err := builderBytesEq(&b, v...); err != nil {
t.Error(err)
}
s := String(b.BytesOrPanic())
for _, w := range []string{"foo", "bar", "baz"} {
var got []byte
if !s.ReadBytes(&got, 3) {
t.Errorf("ReadBytes() = false, want true (w = %v)", w)
}
want := []byte(w)
if !bytes.Equal(got, want) {
t.Errorf("ReadBytes(): got = %v, want %v", got, want)
}
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUint8(t *testing.T) {
var b Builder
b.AddUint8(42)
if err := builderBytesEq(&b, 42); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var v uint8
if !s.ReadUint8(&v) {
t.Error("ReadUint8() = false, want true")
}
if v != 42 {
t.Errorf("v = %d, want 42", v)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUint16(t *testing.T) {
var b Builder
b.AddUint16(65534)
if err := builderBytesEq(&b, 255, 254); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var v uint16
if !s.ReadUint16(&v) {
t.Error("ReadUint16() == false, want true")
}
if v != 65534 {
t.Errorf("v = %d, want 65534", v)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUint24(t *testing.T) {
var b Builder
b.AddUint24(0xfffefd)
if err := builderBytesEq(&b, 255, 254, 253); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var v uint32
if !s.ReadUint24(&v) {
t.Error("ReadUint8() = false, want true")
}
if v != 0xfffefd {
t.Errorf("v = %d, want fffefd", v)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUint24Truncation(t *testing.T) {
var b Builder
b.AddUint24(0x10111213)
if err := builderBytesEq(&b, 0x11, 0x12, 0x13); err != nil {
t.Error(err)
}
}
func TestUint32(t *testing.T) {
var b Builder
b.AddUint32(0xfffefdfc)
if err := builderBytesEq(&b, 255, 254, 253, 252); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var v uint32
if !s.ReadUint32(&v) {
t.Error("ReadUint8() = false, want true")
}
if v != 0xfffefdfc {
t.Errorf("v = %x, want fffefdfc", v)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUMultiple(t *testing.T) {
var b Builder
b.AddUint8(23)
b.AddUint32(0xfffefdfc)
b.AddUint16(42)
if err := builderBytesEq(&b, 23, 255, 254, 253, 252, 0, 42); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var (
x uint8
y uint32
z uint16
)
if !s.ReadUint8(&x) || !s.ReadUint32(&y) || !s.ReadUint16(&z) {
t.Error("ReadUint8() = false, want true")
}
if x != 23 || y != 0xfffefdfc || z != 42 {
t.Errorf("x, y, z = %d, %d, %d; want 23, 4294901244, 5", x, y, z)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUint8LengthPrefixedSimple(t *testing.T) {
var b Builder
b.AddUint8LengthPrefixed(func(c *Builder) {
c.AddUint8(23)
c.AddUint8(42)
})
if err := builderBytesEq(&b, 2, 23, 42); err != nil {
t.Error(err)
}
var base, child String = b.BytesOrPanic(), nil
var x, y uint8
if !base.ReadUint8LengthPrefixed(&child) || !child.ReadUint8(&x) ||
!child.ReadUint8(&y) {
t.Error("parsing failed")
}
if x != 23 || y != 42 {
t.Errorf("want x, y == 23, 42; got %d, %d", x, y)
}
if len(base) != 0 {
t.Errorf("len(base) = %d, want 0", len(base))
}
if len(child) != 0 {
t.Errorf("len(child) = %d, want 0", len(child))
}
}
func TestUint8LengthPrefixedMulti(t *testing.T) {
var b Builder
b.AddUint8LengthPrefixed(func(c *Builder) {
c.AddUint8(23)
c.AddUint8(42)
})
b.AddUint8(5)
b.AddUint8LengthPrefixed(func(c *Builder) {
c.AddUint8(123)
c.AddUint8(234)
})
if err := builderBytesEq(&b, 2, 23, 42, 5, 2, 123, 234); err != nil {
t.Error(err)
}
var s, child String = b.BytesOrPanic(), nil
var u, v, w, x, y uint8
if !s.ReadUint8LengthPrefixed(&child) || !child.ReadUint8(&u) || !child.ReadUint8(&v) ||
!s.ReadUint8(&w) || !s.ReadUint8LengthPrefixed(&child) || !child.ReadUint8(&x) || !child.ReadUint8(&y) {
t.Error("parsing failed")
}
if u != 23 || v != 42 || w != 5 || x != 123 || y != 234 {
t.Errorf("u, v, w, x, y = %d, %d, %d, %d, %d; want 23, 42, 5, 123, 234",
u, v, w, x, y)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
if len(child) != 0 {
t.Errorf("len(child) = %d, want 0", len(child))
}
}
func TestUint8LengthPrefixedNested(t *testing.T) {
var b Builder
b.AddUint8LengthPrefixed(func(c *Builder) {
c.AddUint8(5)
c.AddUint8LengthPrefixed(func(d *Builder) {
d.AddUint8(23)
d.AddUint8(42)
})
c.AddUint8(123)
})
if err := builderBytesEq(&b, 5, 5, 2, 23, 42, 123); err != nil {
t.Error(err)
}
var base, child1, child2 String = b.BytesOrPanic(), nil, nil
var u, v, w, x uint8
if !base.ReadUint8LengthPrefixed(&child1) {
t.Error("parsing base failed")
}
if !child1.ReadUint8(&u) || !child1.ReadUint8LengthPrefixed(&child2) || !child1.ReadUint8(&x) {
t.Error("parsing child1 failed")
}
if !child2.ReadUint8(&v) || !child2.ReadUint8(&w) {
t.Error("parsing child2 failed")
}
if u != 5 || v != 23 || w != 42 || x != 123 {
t.Errorf("u, v, w, x = %d, %d, %d, %d, want 5, 23, 42, 123",
u, v, w, x)
}
if len(base) != 0 {
t.Errorf("len(base) = %d, want 0", len(base))
}
if len(child1) != 0 {
t.Errorf("len(child1) = %d, want 0", len(child1))
}
if len(base) != 0 {
t.Errorf("len(child2) = %d, want 0", len(child2))
}
}
func TestPreallocatedBuffer(t *testing.T) {
var buf [5]byte
b := NewBuilder(buf[0:0])
b.AddUint8(1)
b.AddUint8LengthPrefixed(func(c *Builder) {
c.AddUint8(3)
c.AddUint8(4)
})
b.AddUint16(1286) // Outgrow buf by one byte.
want := []byte{1, 2, 3, 4, 0}
if !bytes.Equal(buf[:], want) {
t.Errorf("buf = %v want %v", buf, want)
}
if err := builderBytesEq(b, 1, 2, 3, 4, 5, 6); err != nil {
t.Error(err)
}
}
func TestWriteWithPendingChild(t *testing.T) {
var b Builder
b.AddUint8LengthPrefixed(func(c *Builder) {
c.AddUint8LengthPrefixed(func(d *Builder) {
defer func() {
if recover() == nil {
t.Errorf("recover() = nil, want error; c.AddUint8() did not panic")
}
}()
c.AddUint8(2) // panics
defer func() {
if recover() == nil {
t.Errorf("recover() = nil, want error; b.AddUint8() did not panic")
}
}()
b.AddUint8(2) // panics
})
defer func() {
if recover() == nil {
t.Errorf("recover() = nil, want error; b.AddUint8() did not panic")
}
}()
b.AddUint8(2) // panics
})
}
// ASN.1
func TestASN1Int64(t *testing.T) {
tests := []struct {
in int64
want []byte
}{
{-0x800000, []byte{2, 3, 128, 0, 0}},
{-256, []byte{2, 2, 255, 0}},
{-129, []byte{2, 2, 255, 127}},
{-128, []byte{2, 1, 128}},
{-1, []byte{2, 1, 255}},
{0, []byte{2, 1, 0}},
{1, []byte{2, 1, 1}},
{2, []byte{2, 1, 2}},
{127, []byte{2, 1, 127}},
{128, []byte{2, 2, 0, 128}},
{256, []byte{2, 2, 1, 0}},
{0x800000, []byte{2, 4, 0, 128, 0, 0}},
}
for i, tt := range tests {
var b Builder
b.AddASN1Int64(tt.in)
if err := builderBytesEq(&b, tt.want...); err != nil {
t.Errorf("%v, (i = %d; in = %v)", err, i, tt.in)
}
var n int64
s := String(b.BytesOrPanic())
ok := s.ReadASN1Integer(&n)
if !ok || n != tt.in {
t.Errorf("s.ReadASN1Integer(&n) = %v, n = %d; want true, n = %d (i = %d)",
ok, n, tt.in, i)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
}
func TestASN1Uint64(t *testing.T) {
tests := []struct {
in uint64
want []byte
}{
{0, []byte{2, 1, 0}},
{1, []byte{2, 1, 1}},
{2, []byte{2, 1, 2}},
{127, []byte{2, 1, 127}},
{128, []byte{2, 2, 0, 128}},
{256, []byte{2, 2, 1, 0}},
{0x800000, []byte{2, 4, 0, 128, 0, 0}},
{0x7fffffffffffffff, []byte{2, 8, 127, 255, 255, 255, 255, 255, 255, 255}},
{0x8000000000000000, []byte{2, 9, 0, 128, 0, 0, 0, 0, 0, 0, 0}},
{0xffffffffffffffff, []byte{2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255}},
}
for i, tt := range tests {
var b Builder
b.AddASN1Uint64(tt.in)
if err := builderBytesEq(&b, tt.want...); err != nil {
t.Errorf("%v, (i = %d; in = %v)", err, i, tt.in)
}
var n uint64
s := String(b.BytesOrPanic())
ok := s.ReadASN1Integer(&n)
if !ok || n != tt.in {
t.Errorf("s.ReadASN1Integer(&n) = %v, n = %d; want true, n = %d (i = %d)",
ok, n, tt.in, i)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
}

120
vendor/golang.org/x/crypto/cryptobyte/example_test.go generated vendored Normal file
View file

@ -0,0 +1,120 @@
// Copyright 2017 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 cryptobyte_test
import (
"encoding/asn1"
"fmt"
"golang.org/x/crypto/cryptobyte"
)
func ExampleString_lengthPrefixed() {
// This is an example of parsing length-prefixed data (as found in, for
// example, TLS). Imagine a 16-bit prefixed series of 8-bit prefixed
// strings.
input := cryptobyte.String([]byte{0, 12, 5, 'h', 'e', 'l', 'l', 'o', 5, 'w', 'o', 'r', 'l', 'd'})
var result []string
var values cryptobyte.String
if !input.ReadUint16LengthPrefixed(&values) ||
!input.Empty() {
panic("bad format")
}
for !values.Empty() {
var value cryptobyte.String
if !values.ReadUint8LengthPrefixed(&value) {
panic("bad format")
}
result = append(result, string(value))
}
// Output: []string{"hello", "world"}
fmt.Printf("%#v\n", result)
}
func ExampleString_asn1() {
// This is an example of parsing ASN.1 data that looks like:
// Foo ::= SEQUENCE {
// version [6] INTEGER DEFAULT 0
// data OCTET STRING
// }
input := cryptobyte.String([]byte{0x30, 12, 0xa6, 3, 2, 1, 2, 4, 5, 'h', 'e', 'l', 'l', 'o'})
var (
version int64
data, inner, versionBytes cryptobyte.String
haveVersion bool
)
if !input.ReadASN1(&inner, cryptobyte.Tag(asn1.TagSequence).Constructed()) ||
!input.Empty() ||
!inner.ReadOptionalASN1(&versionBytes, &haveVersion, cryptobyte.Tag(6).Constructed().ContextSpecific()) ||
(haveVersion && !versionBytes.ReadASN1Integer(&version)) ||
(haveVersion && !versionBytes.Empty()) ||
!inner.ReadASN1(&data, asn1.TagOctetString) ||
!inner.Empty() {
panic("bad format")
}
// Output: haveVersion: true, version: 2, data: hello
fmt.Printf("haveVersion: %t, version: %d, data: %s\n", haveVersion, version, string(data))
}
func ExampleBuilder_asn1() {
// This is an example of building ASN.1 data that looks like:
// Foo ::= SEQUENCE {
// version [6] INTEGER DEFAULT 0
// data OCTET STRING
// }
version := int64(2)
data := []byte("hello")
const defaultVersion = 0
var b cryptobyte.Builder
b.AddASN1(cryptobyte.Tag(asn1.TagSequence).Constructed(), func(b *cryptobyte.Builder) {
if version != defaultVersion {
b.AddASN1(cryptobyte.Tag(6).Constructed().ContextSpecific(), func(b *cryptobyte.Builder) {
b.AddASN1Int64(version)
})
}
b.AddASN1OctetString(data)
})
result, err := b.Bytes()
if err != nil {
panic(err)
}
// Output: 300ca603020102040568656c6c6f
fmt.Printf("%x\n", result)
}
func ExampleBuilder_lengthPrefixed() {
// This is an example of building length-prefixed data (as found in,
// for example, TLS). Imagine a 16-bit prefixed series of 8-bit
// prefixed strings.
input := []string{"hello", "world"}
var b cryptobyte.Builder
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
for _, value := range input {
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes([]byte(value))
})
}
})
result, err := b.Bytes()
if err != nil {
panic(err)
}
// Output: 000c0568656c6c6f05776f726c64
fmt.Printf("%x\n", result)
}

157
vendor/golang.org/x/crypto/cryptobyte/string.go generated vendored Normal file
View file

@ -0,0 +1,157 @@
// Copyright 2017 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 cryptobyte implements building and parsing of byte strings for
// DER-encoded ASN.1 and TLS messages. See the examples for the Builder and
// String types to get started.
package cryptobyte // import "golang.org/x/crypto/cryptobyte"
// String represents a string of bytes. It provides methods for parsing
// fixed-length and length-prefixed values from it.
type String []byte
// read advances a String by n bytes and returns them. If less than n bytes
// remain, it returns nil.
func (s *String) read(n int) []byte {
if len(*s) < n {
return nil
}
v := (*s)[:n]
*s = (*s)[n:]
return v
}
// Skip advances the String by n byte and reports whether it was successful.
func (s *String) Skip(n int) bool {
return s.read(n) != nil
}
// ReadUint8 decodes an 8-bit value into out and advances over it. It
// returns true on success and false on error.
func (s *String) ReadUint8(out *uint8) bool {
v := s.read(1)
if v == nil {
return false
}
*out = uint8(v[0])
return true
}
// ReadUint16 decodes a big-endian, 16-bit value into out and advances over it.
// It returns true on success and false on error.
func (s *String) ReadUint16(out *uint16) bool {
v := s.read(2)
if v == nil {
return false
}
*out = uint16(v[0])<<8 | uint16(v[1])
return true
}
// ReadUint24 decodes a big-endian, 24-bit value into out and advances over it.
// It returns true on success and false on error.
func (s *String) ReadUint24(out *uint32) bool {
v := s.read(3)
if v == nil {
return false
}
*out = uint32(v[0])<<16 | uint32(v[1])<<8 | uint32(v[2])
return true
}
// ReadUint32 decodes a big-endian, 32-bit value into out and advances over it.
// It returns true on success and false on error.
func (s *String) ReadUint32(out *uint32) bool {
v := s.read(4)
if v == nil {
return false
}
*out = uint32(v[0])<<24 | uint32(v[1])<<16 | uint32(v[2])<<8 | uint32(v[3])
return true
}
func (s *String) readUnsigned(out *uint32, length int) bool {
v := s.read(length)
if v == nil {
return false
}
var result uint32
for i := 0; i < length; i++ {
result <<= 8
result |= uint32(v[i])
}
*out = result
return true
}
func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool {
lenBytes := s.read(lenLen)
if lenBytes == nil {
return false
}
var length uint32
for _, b := range lenBytes {
length = length << 8
length = length | uint32(b)
}
if int(length) < 0 {
// This currently cannot overflow because we read uint24 at most, but check
// anyway in case that changes in the future.
return false
}
v := s.read(int(length))
if v == nil {
return false
}
*outChild = v
return true
}
// ReadUint8LengthPrefixed reads the content of an 8-bit length-prefixed value
// into out and advances over it. It returns true on success and false on
// error.
func (s *String) ReadUint8LengthPrefixed(out *String) bool {
return s.readLengthPrefixed(1, out)
}
// ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit
// length-prefixed value into out and advances over it. It returns true on
// success and false on error.
func (s *String) ReadUint16LengthPrefixed(out *String) bool {
return s.readLengthPrefixed(2, out)
}
// ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit
// length-prefixed value into out and advances over it. It returns true on
// success and false on error.
func (s *String) ReadUint24LengthPrefixed(out *String) bool {
return s.readLengthPrefixed(3, out)
}
// ReadBytes reads n bytes into out and advances over them. It returns true on
// success and false and error.
func (s *String) ReadBytes(out *[]byte, n int) bool {
v := s.read(n)
if v == nil {
return false
}
*out = v
return true
}
// CopyBytes copies len(out) bytes into out and advances over them. It returns
// true on success and false on error.
func (s *String) CopyBytes(out []byte) bool {
n := len(out)
v := s.read(n)
if v == nil {
return false
}
return copy(out, v) == n
}
// Empty reports whether the string does not contain any bytes.
func (s String) Empty() bool {
return len(s) == 0
}

8
vendor/golang.org/x/crypto/curve25519/const_amd64.h generated vendored Normal file
View file

@ -0,0 +1,8 @@
// 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.
// This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
#define REDMASK51 0x0007FFFFFFFFFFFF

View file

@ -7,8 +7,8 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
DATA ·REDMASK51(SB)/8, $0x0007FFFFFFFFFFFF // These constants cannot be encoded in non-MOVQ immediates.
GLOBL ·REDMASK51(SB), 8, $8 // We access them directly from memory instead.
DATA ·_121666_213(SB)/8, $996687872 DATA ·_121666_213(SB)/8, $996687872
GLOBL ·_121666_213(SB), 8, $8 GLOBL ·_121666_213(SB), 8, $8

View file

@ -2,87 +2,64 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This code was translated into a form compatible with 6a from the public
// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
// func cswap(inout *[5]uint64, v uint64) // func cswap(inout *[4][5]uint64, v uint64)
TEXT ·cswap(SB),7,$0 TEXT ·cswap(SB),7,$0
MOVQ inout+0(FP),DI MOVQ inout+0(FP),DI
MOVQ v+8(FP),SI MOVQ v+8(FP),SI
CMPQ SI,$1 SUBQ $1, SI
MOVQ 0(DI),SI NOTQ SI
MOVQ 80(DI),DX MOVQ SI, X15
MOVQ 8(DI),CX PSHUFD $0x44, X15, X15
MOVQ 88(DI),R8
MOVQ SI,R9 MOVOU 0(DI), X0
CMOVQEQ DX,SI MOVOU 16(DI), X2
CMOVQEQ R9,DX MOVOU 32(DI), X4
MOVQ CX,R9 MOVOU 48(DI), X6
CMOVQEQ R8,CX MOVOU 64(DI), X8
CMOVQEQ R9,R8 MOVOU 80(DI), X1
MOVQ SI,0(DI) MOVOU 96(DI), X3
MOVQ DX,80(DI) MOVOU 112(DI), X5
MOVQ CX,8(DI) MOVOU 128(DI), X7
MOVQ R8,88(DI) MOVOU 144(DI), X9
MOVQ 16(DI),SI
MOVQ 96(DI),DX MOVO X1, X10
MOVQ 24(DI),CX MOVO X3, X11
MOVQ 104(DI),R8 MOVO X5, X12
MOVQ SI,R9 MOVO X7, X13
CMOVQEQ DX,SI MOVO X9, X14
CMOVQEQ R9,DX
MOVQ CX,R9 PXOR X0, X10
CMOVQEQ R8,CX PXOR X2, X11
CMOVQEQ R9,R8 PXOR X4, X12
MOVQ SI,16(DI) PXOR X6, X13
MOVQ DX,96(DI) PXOR X8, X14
MOVQ CX,24(DI) PAND X15, X10
MOVQ R8,104(DI) PAND X15, X11
MOVQ 32(DI),SI PAND X15, X12
MOVQ 112(DI),DX PAND X15, X13
MOVQ 40(DI),CX PAND X15, X14
MOVQ 120(DI),R8 PXOR X10, X0
MOVQ SI,R9 PXOR X10, X1
CMOVQEQ DX,SI PXOR X11, X2
CMOVQEQ R9,DX PXOR X11, X3
MOVQ CX,R9 PXOR X12, X4
CMOVQEQ R8,CX PXOR X12, X5
CMOVQEQ R9,R8 PXOR X13, X6
MOVQ SI,32(DI) PXOR X13, X7
MOVQ DX,112(DI) PXOR X14, X8
MOVQ CX,40(DI) PXOR X14, X9
MOVQ R8,120(DI)
MOVQ 48(DI),SI MOVOU X0, 0(DI)
MOVQ 128(DI),DX MOVOU X2, 16(DI)
MOVQ 56(DI),CX MOVOU X4, 32(DI)
MOVQ 136(DI),R8 MOVOU X6, 48(DI)
MOVQ SI,R9 MOVOU X8, 64(DI)
CMOVQEQ DX,SI MOVOU X1, 80(DI)
CMOVQEQ R9,DX MOVOU X3, 96(DI)
MOVQ CX,R9 MOVOU X5, 112(DI)
CMOVQEQ R8,CX MOVOU X7, 128(DI)
CMOVQEQ R9,R8 MOVOU X9, 144(DI)
MOVQ SI,48(DI)
MOVQ DX,128(DI)
MOVQ CX,56(DI)
MOVQ R8,136(DI)
MOVQ 64(DI),SI
MOVQ 144(DI),DX
MOVQ 72(DI),CX
MOVQ 152(DI),R8
MOVQ SI,R9
CMOVQEQ DX,SI
CMOVQEQ R9,DX
MOVQ CX,R9
CMOVQEQ R8,CX
CMOVQEQ R9,R8
MOVQ SI,64(DI)
MOVQ DX,144(DI)
MOVQ CX,72(DI)
MOVQ R8,152(DI)
MOVQ DI,AX
MOVQ SI,DX
RET RET

View file

@ -8,6 +8,10 @@
package curve25519 package curve25519
import (
"encoding/binary"
)
// This code is a port of the public domain, "ref10" implementation of // This code is a port of the public domain, "ref10" implementation of
// curve25519 from SUPERCOP 20130419 by D. J. Bernstein. // curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
@ -50,17 +54,11 @@ func feCopy(dst, src *fieldElement) {
// //
// Preconditions: b in {0,1}. // Preconditions: b in {0,1}.
func feCSwap(f, g *fieldElement, b int32) { func feCSwap(f, g *fieldElement, b int32) {
var x fieldElement
b = -b b = -b
for i := range x {
x[i] = b & (f[i] ^ g[i])
}
for i := range f { for i := range f {
f[i] ^= x[i] t := b & (f[i] ^ g[i])
} f[i] ^= t
for i := range g { g[i] ^= t
g[i] ^= x[i]
} }
} }
@ -75,12 +73,7 @@ func load3(in []byte) int64 {
// load4 reads a 32-bit, little-endian value from in. // load4 reads a 32-bit, little-endian value from in.
func load4(in []byte) int64 { func load4(in []byte) int64 {
var r int64 return int64(binary.LittleEndian.Uint32(in))
r = int64(in[0])
r |= int64(in[1]) << 8
r |= int64(in[2]) << 16
r |= int64(in[3]) << 24
return r
} }
func feFromBytes(dst *fieldElement, src *[32]byte) { func feFromBytes(dst *fieldElement, src *[32]byte) {

View file

@ -27,3 +27,13 @@ func TestBaseScalarMult(t *testing.T) {
t.Errorf("incorrect result: got %s, want %s", result, expectedHex) t.Errorf("incorrect result: got %s, want %s", result, expectedHex)
} }
} }
func BenchmarkScalarBaseMult(b *testing.B) {
var in, out [32]byte
in[0] = 1
b.SetBytes(32)
for i := 0; i < b.N; i++ {
ScalarBaseMult(&out, &in)
}
}

View file

@ -7,6 +7,8 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
#include "const_amd64.h"
// func freeze(inout *[5]uint64) // func freeze(inout *[5]uint64)
TEXT ·freeze(SB),7,$0-8 TEXT ·freeze(SB),7,$0-8
MOVQ inout+0(FP), DI MOVQ inout+0(FP), DI
@ -16,7 +18,7 @@ TEXT ·freeze(SB),7,$0-8
MOVQ 16(DI),CX MOVQ 16(DI),CX
MOVQ 24(DI),R8 MOVQ 24(DI),R8
MOVQ 32(DI),R9 MOVQ 32(DI),R9
MOVQ ·REDMASK51(SB),AX MOVQ $REDMASK51,AX
MOVQ AX,R10 MOVQ AX,R10
SUBQ $18,R10 SUBQ $18,R10
MOVQ $3,R11 MOVQ $3,R11

View file

@ -7,6 +7,8 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
#include "const_amd64.h"
// func ladderstep(inout *[5][5]uint64) // func ladderstep(inout *[5][5]uint64)
TEXT ·ladderstep(SB),0,$296-8 TEXT ·ladderstep(SB),0,$296-8
MOVQ inout+0(FP),DI MOVQ inout+0(FP),DI
@ -118,7 +120,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 72(SP) MULQ 72(SP)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -233,7 +235,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 32(SP) MULQ 32(SP)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -438,7 +440,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 72(SP) MULQ 72(SP)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -588,7 +590,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 32(SP) MULQ 32(SP)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -728,7 +730,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 152(DI) MULQ 152(DI)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -843,7 +845,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 192(DI) MULQ 192(DI)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -993,7 +995,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 32(DI) MULQ 32(DI)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -1143,7 +1145,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 112(SP) MULQ 112(SP)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
@ -1329,7 +1331,7 @@ TEXT ·ladderstep(SB),0,$296-8
MULQ 192(SP) MULQ 192(SP)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ ·REDMASK51(SB),DX MOVQ $REDMASK51,DX
SHLQ $13,CX:SI SHLQ $13,CX:SI
ANDQ DX,SI ANDQ DX,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8

View file

@ -7,6 +7,8 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
#include "const_amd64.h"
// func mul(dest, a, b *[5]uint64) // func mul(dest, a, b *[5]uint64)
TEXT ·mul(SB),0,$16-24 TEXT ·mul(SB),0,$16-24
MOVQ dest+0(FP), DI MOVQ dest+0(FP), DI
@ -121,7 +123,7 @@ TEXT ·mul(SB),0,$16-24
MULQ 32(CX) MULQ 32(CX)
ADDQ AX,R14 ADDQ AX,R14
ADCQ DX,R15 ADCQ DX,R15
MOVQ ·REDMASK51(SB),SI MOVQ $REDMASK51,SI
SHLQ $13,R9:R8 SHLQ $13,R9:R8
ANDQ SI,R8 ANDQ SI,R8
SHLQ $13,R11:R10 SHLQ $13,R11:R10

View file

@ -7,6 +7,8 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
#include "const_amd64.h"
// func square(out, in *[5]uint64) // func square(out, in *[5]uint64)
TEXT ·square(SB),7,$0-16 TEXT ·square(SB),7,$0-16
MOVQ out+0(FP), DI MOVQ out+0(FP), DI
@ -84,7 +86,7 @@ TEXT ·square(SB),7,$0-16
MULQ 32(SI) MULQ 32(SI)
ADDQ AX,R13 ADDQ AX,R13
ADCQ DX,R14 ADCQ DX,R14
MOVQ ·REDMASK51(SB),SI MOVQ $REDMASK51,SI
SHLQ $13,R8:CX SHLQ $13,R8:CX
ANDQ SI,CX ANDQ SI,CX
SHLQ $13,R10:R9 SHLQ $13,R10:R9

95
vendor/golang.org/x/crypto/nacl/box/example_test.go generated vendored Normal file
View file

@ -0,0 +1,95 @@
package box_test
import (
crypto_rand "crypto/rand" // Custom so it's clear which rand we're using.
"fmt"
"io"
"golang.org/x/crypto/nacl/box"
)
func Example() {
senderPublicKey, senderPrivateKey, err := box.GenerateKey(crypto_rand.Reader)
if err != nil {
panic(err)
}
recipientPublicKey, recipientPrivateKey, err := box.GenerateKey(crypto_rand.Reader)
if err != nil {
panic(err)
}
// You must use a different nonce for each message you encrypt with the
// same key. Since the nonce here is 192 bits long, a random value
// provides a sufficiently small probability of repeats.
var nonce [24]byte
if _, err := io.ReadFull(crypto_rand.Reader, nonce[:]); err != nil {
panic(err)
}
msg := []byte("Alas, poor Yorick! I knew him, Horatio")
// This encrypts msg and appends the result to the nonce.
encrypted := box.Seal(nonce[:], msg, &nonce, recipientPublicKey, senderPrivateKey)
// The recipient can decrypt the message using their private key and the
// sender's public key. When you decrypt, you must use the same nonce you
// used to encrypt the message. One way to achieve this is to store the
// nonce alongside the encrypted message. Above, we stored the nonce in the
// first 24 bytes of the encrypted text.
var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24])
decrypted, ok := box.Open(nil, encrypted[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
if !ok {
panic("decryption error")
}
fmt.Println(string(decrypted))
// Output: Alas, poor Yorick! I knew him, Horatio
}
func Example_precompute() {
senderPublicKey, senderPrivateKey, err := box.GenerateKey(crypto_rand.Reader)
if err != nil {
panic(err)
}
recipientPublicKey, recipientPrivateKey, err := box.GenerateKey(crypto_rand.Reader)
if err != nil {
panic(err)
}
// The shared key can be used to speed up processing when using the same
// pair of keys repeatedly.
sharedEncryptKey := new([32]byte)
box.Precompute(sharedEncryptKey, recipientPublicKey, senderPrivateKey)
// You must use a different nonce for each message you encrypt with the
// same key. Since the nonce here is 192 bits long, a random value
// provides a sufficiently small probability of repeats.
var nonce [24]byte
if _, err := io.ReadFull(crypto_rand.Reader, nonce[:]); err != nil {
panic(err)
}
msg := []byte("A fellow of infinite jest, of most excellent fancy")
// This encrypts msg and appends the result to the nonce.
encrypted := box.SealAfterPrecomputation(nonce[:], msg, &nonce, sharedEncryptKey)
// The shared key can be used to speed up processing when using the same
// pair of keys repeatedly.
var sharedDecryptKey [32]byte
box.Precompute(&sharedDecryptKey, senderPublicKey, recipientPrivateKey)
// The recipient can decrypt the message using the shared key. When you
// decrypt, you must use the same nonce you used to encrypt the message.
// One way to achieve this is to store the nonce alongside the encrypted
// message. Above, we stored the nonce in the first 24 bytes of the
// encrypted text.
var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24])
decrypted, ok := box.OpenAfterPrecomputation(nil, encrypted[24:], &decryptNonce, &sharedDecryptKey)
if !ok {
panic("decryption error")
}
fmt.Println(string(decrypted))
// Output: A fellow of infinite jest, of most excellent fancy
}

View file

@ -43,7 +43,7 @@ func Example() {
// 24 bytes of the encrypted text. // 24 bytes of the encrypted text.
var decryptNonce [24]byte var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24]) copy(decryptNonce[:], encrypted[:24])
decrypted, ok := secretbox.Open([]byte{}, encrypted[24:], &decryptNonce, &secretKey) decrypted, ok := secretbox.Open(nil, encrypted[24:], &decryptNonce, &secretKey)
if !ok { if !ok {
panic("decryption error") panic("decryption error")
} }

View file

@ -89,3 +89,66 @@ func TestAppend(t *testing.T) {
t.Fatalf("Seal didn't correctly append with sufficient capacity.") t.Fatalf("Seal didn't correctly append with sufficient capacity.")
} }
} }
func benchmarkSealSize(b *testing.B, size int) {
message := make([]byte, size)
out := make([]byte, size+Overhead)
var nonce [24]byte
var key [32]byte
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
out = Seal(out[:0], message, &nonce, &key)
}
}
func BenchmarkSeal8Bytes(b *testing.B) {
benchmarkSealSize(b, 8)
}
func BenchmarkSeal100Bytes(b *testing.B) {
benchmarkSealSize(b, 100)
}
func BenchmarkSeal1K(b *testing.B) {
benchmarkSealSize(b, 1024)
}
func BenchmarkSeal8K(b *testing.B) {
benchmarkSealSize(b, 8192)
}
func benchmarkOpenSize(b *testing.B, size int) {
msg := make([]byte, size)
result := make([]byte, size)
var nonce [24]byte
var key [32]byte
box := Seal(nil, msg, &nonce, &key)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, ok := Open(result[:0], box, &nonce, &key); !ok {
panic("Open failed")
}
}
}
func BenchmarkOpen8Bytes(b *testing.B) {
benchmarkOpenSize(b, 8)
}
func BenchmarkOpen100Bytes(b *testing.B) {
benchmarkOpenSize(b, 100)
}
func BenchmarkOpen1K(b *testing.B) {
benchmarkOpenSize(b, 1024)
}
func BenchmarkOpen8K(b *testing.B) {
benchmarkOpenSize(b, 8192)
}

View file

@ -450,8 +450,8 @@ func ParseRequest(bytes []byte) (*Request, error) {
// then the signature over the response is checked. If issuer is not nil then // then the signature over the response is checked. If issuer is not nil then
// it will be used to validate the signature or embedded certificate. // it will be used to validate the signature or embedded certificate.
// //
// Invalid signatures or parse failures will result in a ParseError. Error // Invalid responses and parse failures will result in a ParseError.
// responses will result in a ResponseError. // Error responses will result in a ResponseError.
func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) { func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
return ParseResponseForCert(bytes, nil, issuer) return ParseResponseForCert(bytes, nil, issuer)
} }
@ -462,8 +462,8 @@ func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
// issuer is not nil then it will be used to validate the signature or embedded // issuer is not nil then it will be used to validate the signature or embedded
// certificate. // certificate.
// //
// Invalid signatures or parse failures will result in a ParseError. Error // Invalid responses and parse failures will result in a ParseError.
// responses will result in a ResponseError. // Error responses will result in a ResponseError.
func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) { func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) {
var resp responseASN1 var resp responseASN1
rest, err := asn1.Unmarshal(bytes, &resp) rest, err := asn1.Unmarshal(bytes, &resp)
@ -496,10 +496,32 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
return nil, ParseError("OCSP response contains bad number of responses") return nil, ParseError("OCSP response contains bad number of responses")
} }
var singleResp singleResponse
if cert == nil {
singleResp = basicResp.TBSResponseData.Responses[0]
} else {
match := false
for _, resp := range basicResp.TBSResponseData.Responses {
if cert == nil || cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 {
singleResp = resp
match = true
break
}
}
if !match {
return nil, ParseError("no response matching the supplied certificate")
}
}
ret := &Response{ ret := &Response{
TBSResponseData: basicResp.TBSResponseData.Raw, TBSResponseData: basicResp.TBSResponseData.Raw,
Signature: basicResp.Signature.RightAlign(), Signature: basicResp.Signature.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm), SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm),
Extensions: singleResp.SingleExtensions,
SerialNumber: singleResp.CertID.SerialNumber,
ProducedAt: basicResp.TBSResponseData.ProducedAt,
ThisUpdate: singleResp.ThisUpdate,
NextUpdate: singleResp.NextUpdate,
} }
// Handle the ResponderID CHOICE tag. ResponderID can be flattened into // Handle the ResponderID CHOICE tag. ResponderID can be flattened into
@ -542,25 +564,14 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
} }
} }
var r singleResponse for _, ext := range singleResp.SingleExtensions {
for _, resp := range basicResp.TBSResponseData.Responses {
if cert == nil || cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 {
r = resp
break
}
}
for _, ext := range r.SingleExtensions {
if ext.Critical { if ext.Critical {
return nil, ParseError("unsupported critical extension") return nil, ParseError("unsupported critical extension")
} }
} }
ret.Extensions = r.SingleExtensions
ret.SerialNumber = r.CertID.SerialNumber
for h, oid := range hashOIDs { for h, oid := range hashOIDs {
if r.CertID.HashAlgorithm.Algorithm.Equal(oid) { if singleResp.CertID.HashAlgorithm.Algorithm.Equal(oid) {
ret.IssuerHash = h ret.IssuerHash = h
break break
} }
@ -570,20 +581,16 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
} }
switch { switch {
case bool(r.Good): case bool(singleResp.Good):
ret.Status = Good ret.Status = Good
case bool(r.Unknown): case bool(singleResp.Unknown):
ret.Status = Unknown ret.Status = Unknown
default: default:
ret.Status = Revoked ret.Status = Revoked
ret.RevokedAt = r.Revoked.RevocationTime ret.RevokedAt = singleResp.Revoked.RevocationTime
ret.RevocationReason = int(r.Revoked.Reason) ret.RevocationReason = int(singleResp.Revoked.Reason)
} }
ret.ProducedAt = basicResp.TBSResponseData.ProducedAt
ret.ThisUpdate = r.ThisUpdate
ret.NextUpdate = r.NextUpdate
return ret, nil return ret, nil
} }

View file

@ -225,7 +225,6 @@ func TestOCSPResponse(t *testing.T) {
}, },
} }
producedAt := time.Now().Truncate(time.Minute)
thisUpdate := time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC) thisUpdate := time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC)
nextUpdate := time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC) nextUpdate := time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC)
template := Response{ template := Response{
@ -284,8 +283,9 @@ func TestOCSPResponse(t *testing.T) {
t.Errorf("resp.Extensions: got %v, want %v", resp.Extensions, template.ExtraExtensions) t.Errorf("resp.Extensions: got %v, want %v", resp.Extensions, template.ExtraExtensions)
} }
if !resp.ProducedAt.Equal(producedAt) { delay := time.Since(resp.ProducedAt)
t.Errorf("resp.ProducedAt: got %d, want %d", resp.ProducedAt, producedAt) if delay < -time.Hour || delay > time.Hour {
t.Errorf("resp.ProducedAt: got %s, want close to current time (%s)", resp.ProducedAt, time.Now())
} }
if resp.Status != template.Status { if resp.Status != template.Status {
@ -343,6 +343,21 @@ func TestOCSPDecodeMultiResponse(t *testing.T) {
} }
} }
func TestOCSPDecodeMultiResponseWithoutMatchingCert(t *testing.T) {
wrongCert, _ := hex.DecodeString(startComHex)
cert, err := x509.ParseCertificate(wrongCert)
if err != nil {
t.Fatal(err)
}
responseBytes, _ := hex.DecodeString(ocspMultiResponseHex)
_, err = ParseResponseForCert(responseBytes, cert, nil)
want := ParseError("no response matching the supplied certificate")
if err != want {
t.Errorf("err: got %q, want %q", err, want)
}
}
// This OCSP response was taken from Thawte's public OCSP responder. // This OCSP response was taken from Thawte's public OCSP responder.
// To recreate: // To recreate:
// $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443 // $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443

View file

@ -109,6 +109,10 @@ func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
if err != nil {
return nil, err
}
blocks := make([]*pem.Block, 0, len(bags)) blocks := make([]*pem.Block, 0, len(bags))
for _, bag := range bags { for _, bag := range bags {
block, err := convertBag(&bag, encodedPassword) block, err := convertBag(&bag, encodedPassword)

View file

@ -6,10 +6,14 @@ package poly1305
import ( import (
"bytes" "bytes"
"encoding/hex"
"flag"
"testing" "testing"
"unsafe" "unsafe"
) )
var stressFlag = flag.Bool("stress", false, "run slow stress tests")
var testData = []struct { var testData = []struct {
in, k, correct []byte in, k, correct []byte
}{ }{
@ -39,6 +43,36 @@ var testData = []struct {
[]byte{0x3b, 0x3a, 0x29, 0xe9, 0x3b, 0x21, 0x3a, 0x5c, 0x5c, 0x3b, 0x3b, 0x05, 0x3a, 0x3a, 0x8c, 0x0d}, []byte{0x3b, 0x3a, 0x29, 0xe9, 0x3b, 0x21, 0x3a, 0x5c, 0x5c, 0x3b, 0x3b, 0x05, 0x3a, 0x3a, 0x8c, 0x0d},
[]byte{0x6d, 0xc1, 0x8b, 0x8c, 0x34, 0x4c, 0xd7, 0x99, 0x27, 0x11, 0x8b, 0xbe, 0x84, 0xb7, 0xf3, 0x14}, []byte{0x6d, 0xc1, 0x8b, 0x8c, 0x34, 0x4c, 0xd7, 0x99, 0x27, 0x11, 0x8b, 0xbe, 0x84, 0xb7, 0xf3, 0x14},
}, },
{
// This test generates a result of (2^130-1) % (2^130-5).
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
[]byte{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
{
// This test generates a result of (2^130-6) % (2^130-5).
[]byte{
0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
[]byte{0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
},
{
// This test generates a result of (2^130-5) % (2^130-5).
[]byte{
0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
} }
func testSum(t *testing.T, unaligned bool) { func testSum(t *testing.T, unaligned bool) {
@ -58,6 +92,39 @@ func testSum(t *testing.T, unaligned bool) {
} }
} }
func TestBurnin(t *testing.T) {
// This test can be used to sanity-check significant changes. It can
// take about many minutes to run, even on fast machines. It's disabled
// by default.
if !*stressFlag {
t.Skip("skipping without -stress")
}
var key [32]byte
var input [25]byte
var output [16]byte
for i := range key {
key[i] = 1
}
for i := range input {
input[i] = 2
}
for i := uint64(0); i < 1e10; i++ {
Sum(&output, input[:], &key)
copy(key[0:], output[:])
copy(key[16:], output[:])
copy(input[:], output[:])
copy(input[16:], output[:])
}
const expected = "5e3b866aea0b636d240c83c428f84bfa"
if got := hex.EncodeToString(output[:]); got != expected {
t.Errorf("expected %s, got %s", expected, got)
}
}
func TestSum(t *testing.T) { testSum(t, false) } func TestSum(t *testing.T) { testSum(t, false) }
func TestSumUnaligned(t *testing.T) { testSum(t, true) } func TestSumUnaligned(t *testing.T) { testSum(t, true) }

File diff suppressed because it is too large Load diff

View file

@ -182,7 +182,10 @@ func TestCert(t *testing.T) {
func netPipe() (net.Conn, net.Conn, error) { func netPipe() (net.Conn, net.Conn, error) {
listener, err := net.Listen("tcp", "127.0.0.1:0") listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
return nil, nil, err listener, err = net.Listen("tcp", "[::1]:0")
if err != nil {
return nil, nil, err
}
} }
defer listener.Close() defer listener.Close()
c1, err := net.Dial("tcp", listener.Addr().String()) c1, err := net.Dial("tcp", listener.Addr().String())
@ -200,6 +203,9 @@ func netPipe() (net.Conn, net.Conn, error) {
} }
func TestAuth(t *testing.T) { func TestAuth(t *testing.T) {
agent, _, cleanup := startAgent(t)
defer cleanup()
a, b, err := netPipe() a, b, err := netPipe()
if err != nil { if err != nil {
t.Fatalf("netPipe: %v", err) t.Fatalf("netPipe: %v", err)
@ -208,9 +214,6 @@ func TestAuth(t *testing.T) {
defer a.Close() defer a.Close()
defer b.Close() defer b.Close()
agent, _, cleanup := startAgent(t)
defer cleanup()
if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil { if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
t.Errorf("Add: %v", err) t.Errorf("Add: %v", err)
} }
@ -233,7 +236,9 @@ func TestAuth(t *testing.T) {
conn.Close() conn.Close()
}() }()
conf := ssh.ClientConfig{} conf := ssh.ClientConfig{
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers)) conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers))
conn, _, _, err := ssh.NewClientConn(b, "", &conf) conn, _, _, err := ssh.NewClientConn(b, "", &conf)
if err != nil { if err != nil {

View file

@ -6,20 +6,20 @@ package agent_test
import ( import (
"log" "log"
"os"
"net" "net"
"os"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent" "golang.org/x/crypto/ssh/agent"
) )
func ExampleClientAgent() { func ExampleClientAgent() {
// ssh-agent has a UNIX socket under $SSH_AUTH_SOCK // ssh-agent has a UNIX socket under $SSH_AUTH_SOCK
socket := os.Getenv("SSH_AUTH_SOCK") socket := os.Getenv("SSH_AUTH_SOCK")
conn, err := net.Dial("unix", socket) conn, err := net.Dial("unix", socket)
if err != nil { if err != nil {
log.Fatalf("net.Dial: %v", err) log.Fatalf("net.Dial: %v", err)
} }
agentClient := agent.NewClient(conn) agentClient := agent.NewClient(conn)
config := &ssh.ClientConfig{ config := &ssh.ClientConfig{
User: "username", User: "username",
@ -29,6 +29,7 @@ func ExampleClientAgent() {
// wants it. // wants it.
ssh.PublicKeysCallback(agentClient.Signers), ssh.PublicKeysCallback(agentClient.Signers),
}, },
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
} }
sshc, err := ssh.Dial("tcp", "localhost:22", config) sshc, err := ssh.Dial("tcp", "localhost:22", config)

View file

@ -56,7 +56,9 @@ func TestSetupForwardAgent(t *testing.T) {
incoming <- conn incoming <- conn
}() }()
conf := ssh.ClientConfig{} conf := ssh.ClientConfig{
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
conn, chans, reqs, err := ssh.NewClientConn(b, "", &conf) conn, chans, reqs, err := ssh.NewClientConn(b, "", &conf)
if err != nil { if err != nil {
t.Fatalf("NewClientConn: %v", err) t.Fatalf("NewClientConn: %v", err)

View file

@ -251,10 +251,18 @@ type CertChecker struct {
// for user certificates. // for user certificates.
SupportedCriticalOptions []string SupportedCriticalOptions []string
// IsAuthority should return true if the key is recognized as // IsUserAuthority should return true if the key is recognized as an
// an authority. This allows for certificates to be signed by other // authority for the given user certificate. This allows for
// certificates. // certificates to be signed by other certificates. This must be set
IsAuthority func(auth PublicKey) bool // if this CertChecker will be checking user certificates.
IsUserAuthority func(auth PublicKey) bool
// IsHostAuthority should report whether the key is recognized as
// an authority for this host. This allows for certificates to be
// signed by other keys, and for those other keys to only be valid
// signers for particular hostnames. This must be set if this
// CertChecker will be checking host certificates.
IsHostAuthority func(auth PublicKey, address string) bool
// Clock is used for verifying time stamps. If nil, time.Now // Clock is used for verifying time stamps. If nil, time.Now
// is used. // is used.
@ -268,7 +276,7 @@ type CertChecker struct {
// HostKeyFallback is called when CertChecker.CheckHostKey encounters a // HostKeyFallback is called when CertChecker.CheckHostKey encounters a
// public key that is not a certificate. It must implement host key // public key that is not a certificate. It must implement host key
// validation or else, if nil, all such keys are rejected. // validation or else, if nil, all such keys are rejected.
HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error HostKeyFallback HostKeyCallback
// IsRevoked is called for each certificate so that revocation checking // IsRevoked is called for each certificate so that revocation checking
// can be implemented. It should return true if the given certificate // can be implemented. It should return true if the given certificate
@ -290,8 +298,17 @@ func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey)
if cert.CertType != HostCert { if cert.CertType != HostCert {
return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType)
} }
if !c.IsHostAuthority(cert.SignatureKey, addr) {
return fmt.Errorf("ssh: no authorities for hostname: %v", addr)
}
return c.CheckCert(addr, cert) hostname, _, err := net.SplitHostPort(addr)
if err != nil {
return err
}
// Pass hostname only as principal for host certificates (consistent with OpenSSH)
return c.CheckCert(hostname, cert)
} }
// Authenticate checks a user certificate. Authenticate can be used as // Authenticate checks a user certificate. Authenticate can be used as
@ -308,6 +325,9 @@ func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permis
if cert.CertType != UserCert { if cert.CertType != UserCert {
return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType)
} }
if !c.IsUserAuthority(cert.SignatureKey) {
return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority")
}
if err := c.CheckCert(conn.User(), cert); err != nil { if err := c.CheckCert(conn.User(), cert); err != nil {
return nil, err return nil, err
@ -356,10 +376,6 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
} }
} }
if !c.IsAuthority(cert.SignatureKey) {
return fmt.Errorf("ssh: certificate signed by unrecognized authority")
}
clock := c.Clock clock := c.Clock
if clock == nil { if clock == nil {
clock = time.Now clock = time.Now

View file

@ -104,7 +104,7 @@ func TestValidateCert(t *testing.T) {
t.Fatalf("got %v (%T), want *Certificate", key, key) t.Fatalf("got %v (%T), want *Certificate", key, key)
} }
checker := CertChecker{} checker := CertChecker{}
checker.IsAuthority = func(k PublicKey) bool { checker.IsUserAuthority = func(k PublicKey) bool {
return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal()) return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal())
} }
@ -142,7 +142,7 @@ func TestValidateCertTime(t *testing.T) {
checker := CertChecker{ checker := CertChecker{
Clock: func() time.Time { return time.Unix(ts, 0) }, Clock: func() time.Time { return time.Unix(ts, 0) },
} }
checker.IsAuthority = func(k PublicKey) bool { checker.IsUserAuthority = func(k PublicKey) bool {
return bytes.Equal(k.Marshal(), return bytes.Equal(k.Marshal(),
testPublicKeys["ecdsa"].Marshal()) testPublicKeys["ecdsa"].Marshal())
} }
@ -160,7 +160,7 @@ func TestValidateCertTime(t *testing.T) {
func TestHostKeyCert(t *testing.T) { func TestHostKeyCert(t *testing.T) {
cert := &Certificate{ cert := &Certificate{
ValidPrincipals: []string{"hostname", "hostname.domain"}, ValidPrincipals: []string{"hostname", "hostname.domain", "otherhost"},
Key: testPublicKeys["rsa"], Key: testPublicKeys["rsa"],
ValidBefore: CertTimeInfinity, ValidBefore: CertTimeInfinity,
CertType: HostCert, CertType: HostCert,
@ -168,8 +168,8 @@ func TestHostKeyCert(t *testing.T) {
cert.SignCert(rand.Reader, testSigners["ecdsa"]) cert.SignCert(rand.Reader, testSigners["ecdsa"])
checker := &CertChecker{ checker := &CertChecker{
IsAuthority: func(p PublicKey) bool { IsHostAuthority: func(p PublicKey, addr string) bool {
return bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal()) return addr == "hostname:22" && bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal())
}, },
} }
@ -178,7 +178,14 @@ func TestHostKeyCert(t *testing.T) {
t.Errorf("NewCertSigner: %v", err) t.Errorf("NewCertSigner: %v", err)
} }
for _, name := range []string{"hostname", "otherhost"} { for _, test := range []struct {
addr string
succeed bool
}{
{addr: "hostname:22", succeed: true},
{addr: "otherhost:22", succeed: false}, // The certificate is valid for 'otherhost' as hostname, but we only recognize the authority of the signer for the address 'hostname:22'
{addr: "lasthost:22", succeed: false},
} {
c1, c2, err := netPipe() c1, c2, err := netPipe()
if err != nil { if err != nil {
t.Fatalf("netPipe: %v", err) t.Fatalf("netPipe: %v", err)
@ -201,16 +208,15 @@ func TestHostKeyCert(t *testing.T) {
User: "user", User: "user",
HostKeyCallback: checker.CheckHostKey, HostKeyCallback: checker.CheckHostKey,
} }
_, _, _, err = NewClientConn(c2, name, config) _, _, _, err = NewClientConn(c2, test.addr, config)
succeed := name == "hostname" if (err == nil) != test.succeed {
if (err == nil) != succeed { t.Fatalf("NewClientConn(%q): %v", test.addr, err)
t.Fatalf("NewClientConn(%q): %v", name, err)
} }
err = <-errc err = <-errc
if (err == nil) != succeed { if (err == nil) != test.succeed {
t.Fatalf("NewServerConn(%q): %v", name, err) t.Fatalf("NewServerConn(%q): %v", test.addr, err)
} }
} }
} }

View file

@ -461,8 +461,8 @@ func (m *mux) newChannel(chanType string, direction channelDirection, extraData
pending: newBuffer(), pending: newBuffer(),
extPending: newBuffer(), extPending: newBuffer(),
direction: direction, direction: direction,
incomingRequests: make(chan *Request, 16), incomingRequests: make(chan *Request, chanSize),
msg: make(chan interface{}, 16), msg: make(chan interface{}, chanSize),
chanType: chanType, chanType: chanType,
extraData: extraData, extraData: extraData,
mux: m, mux: m,

View file

@ -135,6 +135,7 @@ const prefixLen = 5
type streamPacketCipher struct { type streamPacketCipher struct {
mac hash.Hash mac hash.Hash
cipher cipher.Stream cipher cipher.Stream
etm bool
// The following members are to avoid per-packet allocations. // The following members are to avoid per-packet allocations.
prefix [prefixLen]byte prefix [prefixLen]byte
@ -150,7 +151,14 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
return nil, err return nil, err
} }
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) var encryptedPaddingLength [1]byte
if s.mac != nil && s.etm {
copy(encryptedPaddingLength[:], s.prefix[4:5])
s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
} else {
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
}
length := binary.BigEndian.Uint32(s.prefix[0:4]) length := binary.BigEndian.Uint32(s.prefix[0:4])
paddingLength := uint32(s.prefix[4]) paddingLength := uint32(s.prefix[4])
@ -159,7 +167,12 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
s.mac.Reset() s.mac.Reset()
binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
s.mac.Write(s.seqNumBytes[:]) s.mac.Write(s.seqNumBytes[:])
s.mac.Write(s.prefix[:]) if s.etm {
s.mac.Write(s.prefix[:4])
s.mac.Write(encryptedPaddingLength[:])
} else {
s.mac.Write(s.prefix[:])
}
macSize = uint32(s.mac.Size()) macSize = uint32(s.mac.Size())
} }
@ -184,10 +197,17 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
} }
mac := s.packetData[length-1:] mac := s.packetData[length-1:]
data := s.packetData[:length-1] data := s.packetData[:length-1]
if s.mac != nil && s.etm {
s.mac.Write(data)
}
s.cipher.XORKeyStream(data, data) s.cipher.XORKeyStream(data, data)
if s.mac != nil { if s.mac != nil {
s.mac.Write(data) if !s.etm {
s.mac.Write(data)
}
s.macResult = s.mac.Sum(s.macResult[:0]) s.macResult = s.mac.Sum(s.macResult[:0])
if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { if subtle.ConstantTimeCompare(s.macResult, mac) != 1 {
return nil, errors.New("ssh: MAC failure") return nil, errors.New("ssh: MAC failure")
@ -203,7 +223,13 @@ func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Rea
return errors.New("ssh: packet too large") return errors.New("ssh: packet too large")
} }
paddingLength := packetSizeMultiple - (prefixLen+len(packet))%packetSizeMultiple aadlen := 0
if s.mac != nil && s.etm {
// packet length is not encrypted for EtM modes
aadlen = 4
}
paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple
if paddingLength < 4 { if paddingLength < 4 {
paddingLength += packetSizeMultiple paddingLength += packetSizeMultiple
} }
@ -220,15 +246,37 @@ func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Rea
s.mac.Reset() s.mac.Reset()
binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
s.mac.Write(s.seqNumBytes[:]) s.mac.Write(s.seqNumBytes[:])
if s.etm {
// For EtM algorithms, the packet length must stay unencrypted,
// but the following data (padding length) must be encrypted
s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
}
s.mac.Write(s.prefix[:]) s.mac.Write(s.prefix[:])
if !s.etm {
// For non-EtM algorithms, the algorithm is applied on unencrypted data
s.mac.Write(packet)
s.mac.Write(padding)
}
}
if !(s.mac != nil && s.etm) {
// For EtM algorithms, the padding length has already been encrypted
// and the packet length must remain unencrypted
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
}
s.cipher.XORKeyStream(packet, packet)
s.cipher.XORKeyStream(padding, padding)
if s.mac != nil && s.etm {
// For EtM algorithms, packet and padding must be encrypted
s.mac.Write(packet) s.mac.Write(packet)
s.mac.Write(padding) s.mac.Write(padding)
} }
s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
s.cipher.XORKeyStream(packet, packet)
s.cipher.XORKeyStream(padding, padding)
if _, err := w.Write(s.prefix[:]); err != nil { if _, err := w.Write(s.prefix[:]); err != nil {
return err return err
} }

View file

@ -26,39 +26,41 @@ func TestPacketCiphers(t *testing.T) {
defer delete(cipherModes, aes128cbcID) defer delete(cipherModes, aes128cbcID)
for cipher := range cipherModes { for cipher := range cipherModes {
kr := &kexResult{Hash: crypto.SHA1} for mac := range macModes {
algs := directionAlgorithms{ kr := &kexResult{Hash: crypto.SHA1}
Cipher: cipher, algs := directionAlgorithms{
MAC: "hmac-sha1", Cipher: cipher,
Compression: "none", MAC: mac,
} Compression: "none",
client, err := newPacketCipher(clientKeys, algs, kr) }
if err != nil { client, err := newPacketCipher(clientKeys, algs, kr)
t.Errorf("newPacketCipher(client, %q): %v", cipher, err) if err != nil {
continue t.Errorf("newPacketCipher(client, %q, %q): %v", cipher, mac, err)
} continue
server, err := newPacketCipher(clientKeys, algs, kr) }
if err != nil { server, err := newPacketCipher(clientKeys, algs, kr)
t.Errorf("newPacketCipher(client, %q): %v", cipher, err) if err != nil {
continue t.Errorf("newPacketCipher(client, %q, %q): %v", cipher, mac, err)
} continue
}
want := "bla bla" want := "bla bla"
input := []byte(want) input := []byte(want)
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
if err := client.writePacket(0, buf, rand.Reader, input); err != nil { if err := client.writePacket(0, buf, rand.Reader, input); err != nil {
t.Errorf("writePacket(%q): %v", cipher, err) t.Errorf("writePacket(%q, %q): %v", cipher, mac, err)
continue continue
} }
packet, err := server.readPacket(0, buf) packet, err := server.readPacket(0, buf)
if err != nil { if err != nil {
t.Errorf("readPacket(%q): %v", cipher, err) t.Errorf("readPacket(%q, %q): %v", cipher, mac, err)
continue continue
} }
if string(packet) != want { if string(packet) != want {
t.Errorf("roundtrip(%q): got %q, want %q", cipher, packet, want) t.Errorf("roundtrip(%q, %q): got %q, want %q", cipher, mac, packet, want)
}
} }
} }
} }

View file

@ -5,6 +5,7 @@
package ssh package ssh
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"net" "net"
@ -13,7 +14,7 @@ import (
) )
// Client implements a traditional SSH client that supports shells, // Client implements a traditional SSH client that supports shells,
// subprocesses, port forwarding and tunneled dialing. // subprocesses, TCP port/streamlocal forwarding and tunneled dialing.
type Client struct { type Client struct {
Conn Conn
@ -40,7 +41,7 @@ func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel {
return nil return nil
} }
ch = make(chan NewChannel, 16) ch = make(chan NewChannel, chanSize)
c.channelHandlers[channelType] = ch c.channelHandlers[channelType] = ch
return ch return ch
} }
@ -59,6 +60,7 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
conn.forwards.closeAll() conn.forwards.closeAll()
}() }()
go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip"))
go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com"))
return conn return conn
} }
@ -68,6 +70,11 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) {
fullConf := *config fullConf := *config
fullConf.SetDefaults() fullConf.SetDefaults()
if fullConf.HostKeyCallback == nil {
c.Close()
return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback")
}
conn := &connection{ conn := &connection{
sshConn: sshConn{conn: c}, sshConn: sshConn{conn: c},
} }
@ -173,6 +180,13 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) {
return NewClient(c, chans, reqs), nil return NewClient(c, chans, reqs), nil
} }
// HostKeyCallback is the function type used for verifying server
// keys. A HostKeyCallback must return nil if the host key is OK, or
// an error to reject it. It receives the hostname as passed to Dial
// or NewClientConn. The remote address is the RemoteAddr of the
// net.Conn underlying the the SSH connection.
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
// A ClientConfig structure is used to configure a Client. It must not be // A ClientConfig structure is used to configure a Client. It must not be
// modified after having been passed to an SSH function. // modified after having been passed to an SSH function.
type ClientConfig struct { type ClientConfig struct {
@ -188,10 +202,12 @@ type ClientConfig struct {
// be used during authentication. // be used during authentication.
Auth []AuthMethod Auth []AuthMethod
// HostKeyCallback, if not nil, is called during the cryptographic // HostKeyCallback is called during the cryptographic
// handshake to validate the server's host key. A nil HostKeyCallback // handshake to validate the server's host key. The client
// implies that all host keys are accepted. // configuration must supply this callback for the connection
HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error // to succeed. The functions InsecureIgnoreHostKey or
// FixedHostKey can be used for simplistic host key checks.
HostKeyCallback HostKeyCallback
// ClientVersion contains the version identification string that will // ClientVersion contains the version identification string that will
// be used for the connection. If empty, a reasonable default is used. // be used for the connection. If empty, a reasonable default is used.
@ -209,3 +225,33 @@ type ClientConfig struct {
// A Timeout of zero means no timeout. // A Timeout of zero means no timeout.
Timeout time.Duration Timeout time.Duration
} }
// InsecureIgnoreHostKey returns a function that can be used for
// ClientConfig.HostKeyCallback to accept any host key. It should
// not be used for production code.
func InsecureIgnoreHostKey() HostKeyCallback {
return func(hostname string, remote net.Addr, key PublicKey) error {
return nil
}
}
type fixedHostKey struct {
key PublicKey
}
func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error {
if f.key == nil {
return fmt.Errorf("ssh: required host key was nil")
}
if !bytes.Equal(key.Marshal(), f.key.Marshal()) {
return fmt.Errorf("ssh: host key mismatch")
}
return nil
}
// FixedHostKey returns a function for use in
// ClientConfig.HostKeyCallback to accept only a specific host key.
func FixedHostKey(key PublicKey) HostKeyCallback {
hk := &fixedHostKey{key}
return hk.check
}

View file

@ -179,31 +179,26 @@ func (cb publicKeyCallback) method() string {
} }
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
// Authentication is performed in two stages. The first stage sends an // Authentication is performed by sending an enquiry to test if a key is
// enquiry to test if each key is acceptable to the remote. The second // acceptable to the remote. If the key is acceptable, the client will
// stage attempts to authenticate with the valid keys obtained in the // attempt to authenticate with the valid key. If not the client will repeat
// first stage. // the process with the remaining keys.
signers, err := cb() signers, err := cb()
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
var validKeys []Signer
for _, signer := range signers {
if ok, err := validateKey(signer.PublicKey(), user, c); ok {
validKeys = append(validKeys, signer)
} else {
if err != nil {
return false, nil, err
}
}
}
// methods that may continue if this auth is not successful.
var methods []string var methods []string
for _, signer := range validKeys { for _, signer := range signers {
pub := signer.PublicKey() ok, err := validateKey(signer.PublicKey(), user, c)
if err != nil {
return false, nil, err
}
if !ok {
continue
}
pub := signer.PublicKey()
pubKey := pub.Marshal() pubKey := pub.Marshal()
sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{
User: user, User: user,
@ -236,13 +231,29 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
if success {
// If authentication succeeds or the list of available methods does not
// contain the "publickey" method, do not attempt to authenticate with any
// other keys. According to RFC 4252 Section 7, the latter can occur when
// additional authentication methods are required.
if success || !containsMethod(methods, cb.method()) {
return success, methods, err return success, methods, err
} }
} }
return false, methods, nil return false, methods, nil
} }
func containsMethod(methods []string, method string) bool {
for _, m := range methods {
if m == method {
return true
}
}
return false
}
// validateKey validates the key provided is acceptable to the server. // validateKey validates the key provided is acceptable to the server.
func validateKey(key PublicKey, user string, c packetConn) (bool, error) { func validateKey(key PublicKey, user string, c packetConn) (bool, error) {
pubKey := key.Marshal() pubKey := key.Marshal()

Some files were not shown because too many files have changed in this diff Show more