@@ -74,6 +74,23 @@ WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP] | |||
; List of reasons why a Pull Request or Issue can be locked | |||
LOCK_REASONS=Too heated,Off-topic,Resolved,Spam | |||
[cors] | |||
; More information about CORS can be found here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#The_HTTP_response_headers | |||
; enable cors headers (disabled by default) | |||
ENABLED=false | |||
; scheme of allowed requests | |||
SCHEME=http | |||
; list of requesting domains that are allowed | |||
ALLOW_DOMAIN=* | |||
; allow subdomains of headers listed above to request | |||
ALLOW_SUBDOMAIN=false | |||
; list of methods allowed to request | |||
METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS | |||
; max time to cache response | |||
MAX_AGE=10m | |||
; allow request with credentials | |||
ALLOW_CREDENTIALS=false | |||
[ui] | |||
; Number of repositories that are displayed on one explore page | |||
EXPLORE_PAGING_NUM = 20 |
@@ -76,6 +76,16 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | |||
- `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked | |||
## CORS (`cors`) | |||
- `ENABLED`: **false**: enable cors headers (disabled by default) | |||
- `SCHEME`: **http**: scheme of allowed requests | |||
- `ALLOW_DOMAIN`: **\***: list of requesting domains that are allowed | |||
- `ALLOW_SUBDOMAIN`: **false**: allow subdomains of headers listed above to request | |||
- `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: list of methods allowed to request | |||
- `MAX_AGE`: **10m**: max time to cache response | |||
- `ALLOW_CREDENTIALS`: **false**: allow request with credentials | |||
## UI (`ui`) | |||
- `EXPLORE_PAGING_NUM`: **20**: Number of repositories that are shown in one explore page. |
@@ -7,7 +7,7 @@ require ( | |||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 | |||
github.com/RoaringBitmap/roaring v0.4.7 // indirect | |||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca | |||
github.com/Unknwon/com v0.0.0-20170819223952-7677a1d7c113 | |||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 | |||
github.com/Unknwon/i18n v0.0.0-20171114194641-b64d33658966 | |||
github.com/Unknwon/paginater v0.0.0-20151104151617-7748a72e0141 | |||
github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470 // indirect | |||
@@ -48,6 +48,7 @@ require ( | |||
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443 | |||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776 | |||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab | |||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9 | |||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372 | |||
github.com/go-macaron/i18n v0.0.0-20160612092837-ef57533c3b0f | |||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 | |||
@@ -113,17 +114,17 @@ require ( | |||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 // indirect | |||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 | |||
go.etcd.io/bbolt v1.3.2 // indirect | |||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 | |||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 | |||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 | |||
golang.org/x/oauth2 v0.0.0-20181101160152-c453e0c75759 | |||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 | |||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e | |||
golang.org/x/text v0.3.0 | |||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | |||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect | |||
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect | |||
gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0 | |||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df | |||
gopkg.in/ini.v1 v1.31.1 | |||
gopkg.in/ini.v1 v1.42.0 | |||
gopkg.in/ldap.v3 v3.0.2 | |||
gopkg.in/macaron.v1 v1.3.2 | |||
gopkg.in/redis.v2 v2.3.2 // indirect | |||
@@ -135,6 +136,6 @@ require ( | |||
) | |||
replace ( | |||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f => github.com/denisenkom/go-mssqldb v0.0.0-20161128230840-e32ca5036449 | |||
github.com/go-sql-driver/mysql v1.4.0 => github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f | |||
github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20161128230840-e32ca5036449 | |||
github.com/go-sql-driver/mysql => github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f | |||
) |
@@ -7,8 +7,8 @@ github.com/RoaringBitmap/roaring v0.4.7 h1:eGUudvFzvF7Kxh7JjYvXfI1f7l22/2duFby7r | |||
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= | |||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca h1:xU8R31tsvj6TesCBog973+UgI3TXjh/LqN5clki6hcc= | |||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca/go.mod h1:IRSre9/SEhVuy972TVuJLyaPTS73+8Owhe0Y0l9NXHc= | |||
github.com/Unknwon/com v0.0.0-20170819223952-7677a1d7c113 h1:YwXm6KwmrA5R5yJRhcnpqRUHmBXSKciHuWtK9zP5qKQ= | |||
github.com/Unknwon/com v0.0.0-20170819223952-7677a1d7c113/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no= | |||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 h1:1B7wb36fHLSwZfHg6ngZhhtIEHQjiC5H4p7qQGBEffg= | |||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68= | |||
github.com/Unknwon/i18n v0.0.0-20171114194641-b64d33658966 h1:Mp8GNJ/tdTZIEdLdZfykEJaL3mTyEYrSzYNcdoQKpJk= | |||
github.com/Unknwon/i18n v0.0.0-20171114194641-b64d33658966/go.mod h1:SFtfq0zFPsENI7DpE87QM2hcYu5QQ0fRdCgP+P1Hrqo= | |||
github.com/Unknwon/paginater v0.0.0-20151104151617-7748a72e0141 h1:SSvHGK7iMpeypcHjI8UzNMz7zW/K8/dcgqk/82lCYP0= | |||
@@ -105,6 +105,8 @@ github.com/go-macaron/cache v0.0.0-20151013081102-561735312776 h1:UYIHS1r0WotqB5 | |||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776/go.mod h1:hHAsZm/oBZVcY+S7qdQL6Vbg5VrXF6RuKGuqsszt3Ok= | |||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab h1:4VFhsA3GE5Wwq1Ymr8KWCmrOWi1wRLEgdj48LPfQjxI= | |||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab/go.mod h1:j9TJ+0nwUOWBvNnm0bheHIPFf3cC62EQo7n7O6PbjZA= | |||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9 h1:A0QGzY6UHHEil0I2e7C21JenNNG0mmrj5d9SFWTlgr8= | |||
github.com/go-macaron/cors v0.0.0-20190309005821-6fd6a9bfe14e9/go.mod h1:utmMRnVIrXPSfA9MFcpIYKEpKawjKxf62vv62k4707E= | |||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372 h1:acrx8CnDmlKl+BPoOOLEK9Ko+SrWFB5pxRuGkKj4iqo= | |||
github.com/go-macaron/csrf v0.0.0-20180426211211-503617c6b372/go.mod h1:oZGMxI7MBnicI0jJqJvH4qQzyrWKhtiKxLSJKHC+ydc= | |||
github.com/go-macaron/i18n v0.0.0-20160612092837-ef57533c3b0f h1:wDKrZFc9pYJlqFOf7EzGbFMrSFFtyHt3plr2uTdo8Rg= | |||
@@ -148,6 +150,8 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu | |||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= | |||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= | |||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | |||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= | |||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= | |||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= | |||
@@ -178,6 +182,7 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U | |||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= | |||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= | |||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= | |||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | |||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= | |||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | |||
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 h1:vE7J1m7cCpiRVEIr1B5ccDxRpbPsWT5JU3if2Di5nE4= | |||
@@ -281,6 +286,9 @@ github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d h1:qQWKKOvHN7 | |||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY= | |||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= | |||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | |||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= | |||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | |||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= | |||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff h1:86HlEv0yBCry9syNuylzqznKXDK11p6D0DT596yNMys= | |||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= | |||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= | |||
@@ -316,6 +324,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | |||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= | |||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk= | |||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= | |||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
@@ -332,6 +342,8 @@ golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5h | |||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= | |||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts= | |||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | |||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
@@ -353,8 +365,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | |||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | |||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= | |||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= | |||
gopkg.in/ini.v1 v1.31.1 h1:8EY/6KDwKM9Qg4vu1+01ZpsxClC/XV71R+nZ/TL7D4M= | |||
gopkg.in/ini.v1 v1.31.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= | |||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||
gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w= | |||
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= | |||
gopkg.in/macaron.v1 v1.3.2 h1:AvWIaPmwBUA87/OWzePkoxeaw6YJWDfBt1pDFPBnLf8= |
@@ -0,0 +1,22 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package integrations | |||
import ( | |||
"net/http" | |||
"testing" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestCORSNotSet(t *testing.T) { | |||
prepareTestEnv(t) | |||
req := NewRequestf(t, "GET", "/api/v1/version") | |||
session := loginUser(t, "user2") | |||
resp := session.MakeRequest(t, req, http.StatusOK) | |||
assert.Equal(t, resp.Code, http.StatusOK) | |||
corsHeader := resp.Header().Get("Access-Control-Allow-Origin") | |||
assert.Equal(t, corsHeader, "", "Access-Control-Allow-Origin: generated header should match") // header not set | |||
} |
@@ -0,0 +1,41 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package setting | |||
import ( | |||
"time" | |||
"code.gitea.io/gitea/modules/log" | |||
"github.com/go-macaron/cors" | |||
) | |||
var ( | |||
// CORSConfig defines CORS settings | |||
CORSConfig cors.Options | |||
// EnableCORS defines whether CORS settings is enabled or not | |||
EnableCORS bool | |||
) | |||
func newCORSService() { | |||
sec := Cfg.Section("cors") | |||
// Check cors setting. | |||
EnableCORS = sec.Key("ENABLED").MustBool(false) | |||
maxAge := sec.Key("MAX_AGE").MustDuration(10 * time.Minute) | |||
CORSConfig = cors.Options{ | |||
Scheme: sec.Key("SCHEME").String(), | |||
AllowDomain: sec.Key("ALLOW_DOMAIN").String(), | |||
AllowSubdomain: sec.Key("ALLOW_SUBDOMAIN").MustBool(), | |||
Methods: sec.Key("METHODS").Strings(","), | |||
MaxAgeSeconds: int(maxAge.Seconds()), | |||
AllowCredentials: sec.Key("ALLOW_CREDENTIALS").MustBool(), | |||
} | |||
if EnableCORS { | |||
log.Info("CORS Service Enabled") | |||
} | |||
} |
@@ -1006,6 +1006,7 @@ func NewServices() { | |||
NewLogServices(false) | |||
newCacheService() | |||
newSessionService() | |||
newCORSService() | |||
newMailService() | |||
newRegisterMailService() | |||
newNotifyMailService() |
@@ -74,7 +74,8 @@ import ( | |||
"code.gitea.io/gitea/routers/api/v1/user" | |||
"github.com/go-macaron/binding" | |||
"gopkg.in/macaron.v1" | |||
"github.com/go-macaron/cors" | |||
macaron "gopkg.in/macaron.v1" | |||
) | |||
func sudo() macaron.Handler { | |||
@@ -500,6 +501,12 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/swagger", misc.Swagger) //Render V1 by default | |||
} | |||
var handlers []macaron.Handler | |||
if setting.EnableCORS { | |||
handlers = append(handlers, cors.CORS(setting.CORSConfig)) | |||
} | |||
handlers = append(handlers, securityHeaders(), context.APIContexter(), sudo()) | |||
m.Group("/v1", func() { | |||
// Miscellaneous | |||
if setting.API.EnableSwagger { | |||
@@ -841,5 +848,15 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Group("/topics", func() { | |||
m.Get("/search", repo.TopicSearch) | |||
}) | |||
}, context.APIContexter(), sudo()) | |||
}, handlers...) | |||
} | |||
func securityHeaders() macaron.Handler { | |||
return func(ctx *macaron.Context) { | |||
ctx.Resp.Before(func(w macaron.ResponseWriter) { | |||
// CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers | |||
// http://stackoverflow.com/a/3146618/244009 | |||
w.Header().Set("x-content-type-options", "nosniff") | |||
}) | |||
} | |||
} |
@@ -32,7 +32,7 @@ func IsDir(dir string) bool { | |||
return f.IsDir() | |||
} | |||
func statDir(dirPath, recPath string, includeDir, isDirOnly bool) ([]string, error) { | |||
func statDir(dirPath, recPath string, includeDir, isDirOnly, followSymlinks bool) ([]string, error) { | |||
dir, err := os.Open(dirPath) | |||
if err != nil { | |||
return nil, err | |||
@@ -56,13 +56,29 @@ func statDir(dirPath, recPath string, includeDir, isDirOnly bool) ([]string, err | |||
if includeDir { | |||
statList = append(statList, relPath+"/") | |||
} | |||
s, err := statDir(curPath, relPath, includeDir, isDirOnly) | |||
s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks) | |||
if err != nil { | |||
return nil, err | |||
} | |||
statList = append(statList, s...) | |||
} else if !isDirOnly { | |||
statList = append(statList, relPath) | |||
} else if followSymlinks && fi.Mode()&os.ModeSymlink != 0 { | |||
link, err := os.Readlink(curPath) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if IsDir(link) { | |||
if includeDir { | |||
statList = append(statList, relPath+"/") | |||
} | |||
s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks) | |||
if err != nil { | |||
return nil, err | |||
} | |||
statList = append(statList, s...) | |||
} | |||
} | |||
} | |||
return statList, nil | |||
@@ -84,7 +100,26 @@ func StatDir(rootPath string, includeDir ...bool) ([]string, error) { | |||
if len(includeDir) >= 1 { | |||
isIncludeDir = includeDir[0] | |||
} | |||
return statDir(rootPath, "", isIncludeDir, false) | |||
return statDir(rootPath, "", isIncludeDir, false, false) | |||
} | |||
// LstatDir gathers information of given directory by depth-first. | |||
// It returns slice of file list, follows symbolic links and includes subdirectories if enabled; | |||
// it returns error and nil slice when error occurs in underlying functions, | |||
// or given path is not a directory or does not exist. | |||
// | |||
// Slice does not include given path itself. | |||
// If subdirectories is enabled, they will have suffix '/'. | |||
func LstatDir(rootPath string, includeDir ...bool) ([]string, error) { | |||
if !IsDir(rootPath) { | |||
return nil, errors.New("not a directory or does not exist: " + rootPath) | |||
} | |||
isIncludeDir := false | |||
if len(includeDir) >= 1 { | |||
isIncludeDir = includeDir[0] | |||
} | |||
return statDir(rootPath, "", isIncludeDir, false, true) | |||
} | |||
// GetAllSubDirs returns all subdirectories of given root path. | |||
@@ -93,7 +128,17 @@ func GetAllSubDirs(rootPath string) ([]string, error) { | |||
if !IsDir(rootPath) { | |||
return nil, errors.New("not a directory or does not exist: " + rootPath) | |||
} | |||
return statDir(rootPath, "", true, true) | |||
return statDir(rootPath, "", true, true, false) | |||
} | |||
// LgetAllSubDirs returns all subdirectories of given root path, including | |||
// following symbolic links, if any. | |||
// Slice does not include given path itself. | |||
func LgetAllSubDirs(rootPath string) ([]string, error) { | |||
if !IsDir(rootPath) { | |||
return nil, errors.New("not a directory or does not exist: " + rootPath) | |||
} | |||
return statDir(rootPath, "", true, true, true) | |||
} | |||
// GetFileListBySuffix returns an ordered list of file paths. |
@@ -0,0 +1,8 @@ | |||
module github.com/Unknwon/com | |||
require ( | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect | |||
github.com/jtolds/gls v4.2.1+incompatible // indirect | |||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 // indirect | |||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c | |||
) |
@@ -0,0 +1,8 @@ | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | |||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= | |||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | |||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= | |||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | |||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= | |||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= |
@@ -36,7 +36,7 @@ func HtmlEncode(str string) string { | |||
return html.EscapeString(str) | |||
} | |||
// decode string to html chars | |||
// HtmlDecode decodes string to html chars | |||
func HtmlDecode(str string) string { | |||
return html.UnescapeString(str) | |||
} |
@@ -177,7 +177,7 @@ func FetchFiles(client *http.Client, files []RawFile, header http.Header) error | |||
return nil | |||
} | |||
// FetchFiles uses command `curl` to fetch files specified by the rawURL field in parallel. | |||
// FetchFilesCurl uses command `curl` to fetch files specified by the rawURL field in parallel. | |||
func FetchFilesCurl(files []RawFile, curlOptions ...string) error { | |||
ch := make(chan error, len(files)) | |||
for i := range files { |
@@ -14,12 +14,12 @@ | |||
package com | |||
// PowInt is int type of math.Pow function. | |||
// PowInt is int type of math.Pow function. | |||
func PowInt(x int, y int) int { | |||
if y <= 0 { | |||
return 1 | |||
} else { | |||
if y % 2 == 0 { | |||
if y%2 == 0 { | |||
sqrt := PowInt(x, y/2) | |||
return sqrt * sqrt | |||
} else { |
@@ -37,19 +37,19 @@ func init() { | |||
regex_url = regexp.MustCompile(regex_url_pattern) | |||
} | |||
// validate string is an email address, if not return false | |||
// IsEmail validates string is an email address, if not return false | |||
// basically validation can match 99% cases | |||
func IsEmail(email string) bool { | |||
return regex_email.MatchString(email) | |||
} | |||
// validate string is an email address, if not return false | |||
// IsEmailRFC validates string is an email address, if not return false | |||
// this validation omits RFC 2822 | |||
func IsEmailRFC(email string) bool { | |||
return regex_strict_email.MatchString(email) | |||
} | |||
// validate string is a url link, if not return false | |||
// IsUrl validates string is a url link, if not return false | |||
// simple validation can match 99% cases | |||
func IsUrl(url string) bool { | |||
return regex_url.MatchString(url) |
@@ -44,7 +44,7 @@ func CompareSliceStr(s1, s2 []string) bool { | |||
return true | |||
} | |||
// CompareSliceStr compares two 'string' type slices. | |||
// CompareSliceStrU compares two 'string' type slices. | |||
// It returns true if elements are the same, and ignores the order. | |||
func CompareSliceStrU(s1, s2 []string) bool { | |||
if len(s1) != len(s2) { |
@@ -0,0 +1,12 @@ | |||
# Binaries for programs and plugins | |||
*.exe | |||
*.exe~ | |||
*.dll | |||
*.so | |||
*.dylib | |||
# Test binary, build with `go test -c` | |||
*.test | |||
# Output of the go coverage tool, specifically when used with LiteIDE | |||
*.out |
@@ -0,0 +1,201 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
APPENDIX: How to apply the Apache License to your work. | |||
To apply the Apache License to your work, attach the following | |||
boilerplate notice, with the fields enclosed by brackets "[]" | |||
replaced with your own identifying information. (Don't include | |||
the brackets!) The text should be enclosed in the appropriate | |||
comment syntax for the file format. We also recommend that a | |||
file or class name and description of purpose be included on the | |||
same "printed page" as the copyright notice for easier | |||
identification within third-party archives. | |||
Copyright [yyyy] [name of copyright owner] | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. |
@@ -0,0 +1,2 @@ | |||
# cors | |||
Package cors is a middleware that handles CORS requests & headers for Macaron. |
@@ -0,0 +1,139 @@ | |||
package cors | |||
import ( | |||
"fmt" | |||
"log" | |||
"net/http" | |||
"net/url" | |||
"strconv" | |||
"strings" | |||
macaron "gopkg.in/macaron.v1" | |||
) | |||
const _VERSION = "0.1.0" | |||
func Version() string { | |||
return _VERSION | |||
} | |||
// Options represents a struct for specifying configuration options for the CORS middleware. | |||
type Options struct { | |||
Section string | |||
Scheme string | |||
AllowDomain string | |||
AllowSubdomain bool | |||
Methods []string | |||
MaxAgeSeconds int | |||
AllowCredentials bool | |||
} | |||
func prepareOptions(options []Options) Options { | |||
var opt Options | |||
if len(options) > 0 { | |||
opt = options[0] | |||
} | |||
if len(opt.Section) == 0 { | |||
opt.Section = "cors" | |||
} | |||
sec := macaron.Config().Section(opt.Section) | |||
if len(opt.Scheme) == 0 { | |||
opt.Scheme = sec.Key("SCHEME").MustString("http") | |||
} | |||
if len(opt.AllowDomain) == 0 { | |||
opt.AllowDomain = sec.Key("ALLOW_DOMAIN").MustString("*") | |||
} | |||
if !opt.AllowSubdomain { | |||
opt.AllowSubdomain = sec.Key("ALLOW_SUBDOMAIN").MustBool(false) | |||
} | |||
if len(opt.Methods) == 0 { | |||
opt.Methods = sec.Key("METHODS").Strings(",") | |||
if len(opt.Methods) == 0 { | |||
opt.Methods = []string{ | |||
http.MethodGet, | |||
http.MethodHead, | |||
http.MethodPost, | |||
http.MethodPut, | |||
http.MethodPatch, | |||
http.MethodDelete, | |||
http.MethodOptions, | |||
} | |||
} | |||
} | |||
if opt.MaxAgeSeconds <= 0 { | |||
// cache options response for 600 secs | |||
// ref: https://stackoverflow.com/questions/54300997/is-it-possible-to-cache-http-options-response?noredirect=1#comment95790277_54300997 | |||
// ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age | |||
opt.MaxAgeSeconds = sec.Key("MAX_AGE_SECONDS").MustInt(600) | |||
} | |||
if !opt.AllowCredentials { | |||
opt.AllowCredentials = sec.Key("ALLOW_CREDENTIALS").MustBool(true) | |||
} | |||
return opt | |||
} | |||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin | |||
// https://fetch.spec.whatwg.org/#cors-protocol-and-credentials | |||
// For requests without credentials, the server may specify "*" as a wildcard, thereby allowing any origin to access the resource. | |||
func CORS(options ...Options) macaron.Handler { | |||
opt := prepareOptions(options) | |||
return func(ctx *macaron.Context, log *log.Logger) { | |||
reqOptions := ctx.Req.Method == http.MethodOptions | |||
headers := map[string]string{ | |||
"access-control-allow-methods": strings.Join(opt.Methods, ","), | |||
"access-control-allow-headers": ctx.Req.Header.Get("access-control-request-headers"), | |||
"access-control-max-age": strconv.Itoa(opt.MaxAgeSeconds), | |||
} | |||
if opt.AllowDomain == "*" { | |||
headers["access-control-allow-origin"] = "*" | |||
} else if opt.AllowDomain != "" { | |||
origin := ctx.Req.Header.Get("Origin") | |||
if reqOptions && origin == "" { | |||
respErrorf(ctx, log, http.StatusBadRequest, "missing origin header in CORS request") | |||
return | |||
} | |||
u, err := url.Parse(origin) | |||
if err != nil { | |||
respErrorf(ctx, log, http.StatusBadRequest, "Failed to parse CORS origin header. Reason: %v", err) | |||
return | |||
} | |||
ok := u.Hostname() == opt.AllowDomain || | |||
(opt.AllowSubdomain && strings.HasSuffix(u.Hostname(), "."+opt.AllowDomain)) | |||
if ok { | |||
u.Scheme = opt.Scheme | |||
headers["access-control-allow-origin"] = u.String() | |||
headers["access-control-allow-credentials"] = strconv.FormatBool(opt.AllowCredentials) | |||
headers["vary"] = "Origin" | |||
} | |||
if reqOptions && !ok { | |||
respErrorf(ctx, log, http.StatusBadRequest, "CORS request from prohibited domain %v", origin) | |||
return | |||
} | |||
} | |||
ctx.Resp.Before(func(w macaron.ResponseWriter) { | |||
for k, v := range headers { | |||
w.Header().Set(k, v) | |||
} | |||
}) | |||
if reqOptions { | |||
ctx.Status(200) // return response | |||
} | |||
} | |||
} | |||
func respErrorf(ctx *macaron.Context, log *log.Logger, statusCode int, format string, a ...interface{}) { | |||
msg := fmt.Sprintf(format, a...) | |||
log.Println(msg) | |||
ctx.WriteHeader(statusCode) | |||
_, err := ctx.Write([]byte(msg)) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return | |||
} |
@@ -0,0 +1,11 @@ | |||
module github.com/go-macaron/cors | |||
go 1.12 | |||
require ( | |||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 // indirect | |||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 // indirect | |||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 // indirect | |||
gopkg.in/ini.v1 v1.42.0 // indirect | |||
gopkg.in/macaron.v1 v1.3.2 | |||
) |
@@ -0,0 +1,19 @@ | |||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 h1:1B7wb36fHLSwZfHg6ngZhhtIEHQjiC5H4p7qQGBEffg= | |||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68= | |||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI= | |||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw= | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= | |||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | |||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= | |||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | |||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= | |||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | |||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= | |||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= | |||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk= | |||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= | |||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= | |||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||
gopkg.in/macaron.v1 v1.3.2 h1:AvWIaPmwBUA87/OWzePkoxeaw6YJWDfBt1pDFPBnLf8= | |||
gopkg.in/macaron.v1 v1.3.2/go.mod h1:PrsiawTWAGZs6wFbT5hlr7SQ2Ns9h7cUVtcUu4lQOVo= |
@@ -77,6 +77,10 @@ const ( | |||
type Client struct { | |||
// Key is the account key used to register with a CA and sign requests. | |||
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey. | |||
// | |||
// The following algorithms are supported: | |||
// RS256, ES256, ES384 and ES512. | |||
// See RFC7518 for more details about the algorithms. | |||
Key crypto.Signer | |||
// HTTPClient optionally specifies an HTTP client to use | |||
@@ -124,11 +128,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) { | |||
return *c.dir, nil | |||
} | |||
dirURL := c.DirectoryURL | |||
if dirURL == "" { | |||
dirURL = LetsEncryptURL | |||
} | |||
res, err := c.get(ctx, dirURL, wantStatus(http.StatusOK)) | |||
res, err := c.get(ctx, c.directoryURL(), wantStatus(http.StatusOK)) | |||
if err != nil { | |||
return Directory{}, err | |||
} | |||
@@ -161,6 +161,13 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) { | |||
return *c.dir, nil | |||
} | |||
func (c *Client) directoryURL() string { | |||
if c.DirectoryURL != "" { | |||
return c.DirectoryURL | |||
} | |||
return LetsEncryptURL | |||
} | |||
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format. | |||
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate | |||
// with a different duration. | |||
@@ -319,6 +326,20 @@ func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) { | |||
// a valid authorization (Authorization.Status is StatusValid). If so, the caller | |||
// need not fulfill any challenge and can proceed to requesting a certificate. | |||
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) { | |||
return c.authorize(ctx, "dns", domain) | |||
} | |||
// AuthorizeIP is the same as Authorize but requests IP address authorization. | |||
// Clients which successfully obtain such authorization may request to issue | |||
// a certificate for IP addresses. | |||
// | |||
// See the ACME spec extension for more details about IP address identifiers: | |||
// https://tools.ietf.org/html/draft-ietf-acme-ip. | |||
func (c *Client) AuthorizeIP(ctx context.Context, ipaddr string) (*Authorization, error) { | |||
return c.authorize(ctx, "ip", ipaddr) | |||
} | |||
func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization, error) { | |||
if _, err := c.Discover(ctx); err != nil { | |||
return nil, err | |||
} | |||
@@ -332,7 +353,7 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, | |||
Identifier authzID `json:"identifier"` | |||
}{ | |||
Resource: "new-authz", | |||
Identifier: authzID{Type: "dns", Value: domain}, | |||
Identifier: authzID{Type: typ, Value: val}, | |||
} | |||
res, err := c.post(ctx, c.Key, c.dir.AuthzURL, req, wantStatus(http.StatusCreated)) | |||
if err != nil { | |||
@@ -693,12 +714,18 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun | |||
} | |||
// popNonce returns a nonce value previously stored with c.addNonce | |||
// or fetches a fresh one from the given URL. | |||
// or fetches a fresh one from a URL by issuing a HEAD request. | |||
// It first tries c.directoryURL() and then the provided url if the former fails. | |||
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) | |||
dirURL := c.directoryURL() | |||
v, err := c.fetchNonce(ctx, dirURL) | |||
if err != nil && url != dirURL { | |||
v, err = c.fetchNonce(ctx, url) | |||
} | |||
return v, err | |||
} | |||
var nonce string | |||
for nonce = range c.nonces { |
@@ -69,7 +69,7 @@ func HostWhitelist(hosts ...string) HostPolicy { | |||
} | |||
return func(_ context.Context, host string) error { | |||
if !whitelist[host] { | |||
return errors.New("acme/autocert: host not configured") | |||
return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host) | |||
} | |||
return nil | |||
} |
@@ -25,7 +25,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byt | |||
if err != nil { | |||
return nil, err | |||
} | |||
alg, sha := jwsHasher(key) | |||
alg, sha := jwsHasher(key.Public()) | |||
if alg == "" || !sha.Available() { | |||
return nil, ErrUnsupportedKey | |||
} | |||
@@ -97,13 +97,16 @@ func jwkEncode(pub crypto.PublicKey) (string, error) { | |||
} | |||
// jwsSign signs the digest using the given key. | |||
// It returns ErrUnsupportedKey if the key type is unknown. | |||
// The hash is used only for RSA keys. | |||
// The hash is unused for ECDSA keys. | |||
// | |||
// Note: non-stdlib crypto.Signer implementations are expected to return | |||
// the signature in the format as specified in RFC7518. | |||
// See https://tools.ietf.org/html/rfc7518 for more details. | |||
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) { | |||
switch key := key.(type) { | |||
case *rsa.PrivateKey: | |||
return key.Sign(rand.Reader, digest, hash) | |||
case *ecdsa.PrivateKey: | |||
if key, ok := key.(*ecdsa.PrivateKey); ok { | |||
// The key.Sign method of ecdsa returns ASN1-encoded signature. | |||
// So, we use the package Sign function instead | |||
// to get R and S values directly and format the result accordingly. | |||
r, s, err := ecdsa.Sign(rand.Reader, key, digest) | |||
if err != nil { | |||
return nil, err | |||
@@ -118,18 +121,18 @@ func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) | |||
copy(sig[size*2-len(sb):], sb) | |||
return sig, nil | |||
} | |||
return nil, ErrUnsupportedKey | |||
return key.Sign(rand.Reader, digest, hash) | |||
} | |||
// jwsHasher indicates suitable JWS algorithm name and a hash function | |||
// to use for signing a digest with the provided key. | |||
// It returns ("", 0) if the key is not supported. | |||
func jwsHasher(key crypto.Signer) (string, crypto.Hash) { | |||
switch key := key.(type) { | |||
case *rsa.PrivateKey: | |||
func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) { | |||
switch pub := pub.(type) { | |||
case *rsa.PublicKey: | |||
return "RS256", crypto.SHA256 | |||
case *ecdsa.PrivateKey: | |||
switch key.Params().Name { | |||
case *ecdsa.PublicKey: | |||
switch pub.Params().Name { | |||
case "P-256": | |||
return "ES256", crypto.SHA256 | |||
case "P-384": |
@@ -3,6 +3,14 @@ | |||
// license that can be found in the LICENSE file. | |||
// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm. | |||
// | |||
// Blowfish is a legacy cipher and its short block size makes it vulnerable to | |||
// birthday bound attacks (see https://sweet32.info). It should only be used | |||
// where compatibility with legacy systems, not security, is the goal. | |||
// | |||
// Deprecated: any new system should use AES (from crypto/aes, if necessary in | |||
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from | |||
// golang.org/x/crypto/chacha20poly1305). | |||
package blowfish // import "golang.org/x/crypto/blowfish" | |||
// The code is a port of Bruce Schneier's C implementation. |
@@ -2,8 +2,15 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common | |||
// OpenPGP cipher. | |||
// Package cast5 implements CAST5, as defined in RFC 2144. | |||
// | |||
// CAST5 is a legacy cipher and its short block size makes it vulnerable to | |||
// birthday bound attacks (see https://sweet32.info). It should only be used | |||
// where compatibility with legacy systems, not security, is the goal. | |||
// | |||
// Deprecated: any new system should use AES (from crypto/aes, if necessary in | |||
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from | |||
// golang.org/x/crypto/chacha20poly1305). | |||
package cast5 // import "golang.org/x/crypto/cast5" | |||
import "errors" |
@@ -86,7 +86,7 @@ func feFromBytes(dst *fieldElement, src *[32]byte) { | |||
h6 := load3(src[20:]) << 7 | |||
h7 := load3(src[23:]) << 5 | |||
h8 := load3(src[26:]) << 4 | |||
h9 := load3(src[29:]) << 2 | |||
h9 := (load3(src[29:]) & 0x7fffff) << 2 | |||
var carry [10]int64 | |||
carry[9] = (h9 + 1<<24) >> 25 |
@@ -121,18 +121,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -236,18 +236,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -441,18 +441,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -591,18 +591,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -731,18 +731,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -846,18 +846,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -996,18 +996,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -1146,18 +1146,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX | |||
@@ -1332,18 +1332,18 @@ TEXT ·ladderstep(SB),0,$296-8 | |||
ADDQ AX,R12 | |||
ADCQ DX,R13 | |||
MOVQ $REDMASK51,DX | |||
SHLQ $13,CX:SI | |||
SHLQ $13,SI,CX | |||
ANDQ DX,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ DX,R8 | |||
ADDQ CX,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ DX,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ DX,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ DX,R14 | |||
ADDQ R13,R14 | |||
IMUL3Q $19,R15,CX |
@@ -124,18 +124,18 @@ TEXT ·mul(SB),0,$16-24 | |||
ADDQ AX,R14 | |||
ADCQ DX,R15 | |||
MOVQ $REDMASK51,SI | |||
SHLQ $13,R9:R8 | |||
SHLQ $13,R8,R9 | |||
ANDQ SI,R8 | |||
SHLQ $13,R11:R10 | |||
SHLQ $13,R10,R11 | |||
ANDQ SI,R10 | |||
ADDQ R9,R10 | |||
SHLQ $13,R13:R12 | |||
SHLQ $13,R12,R13 | |||
ANDQ SI,R12 | |||
ADDQ R11,R12 | |||
SHLQ $13,R15:R14 | |||
SHLQ $13,R14,R15 | |||
ANDQ SI,R14 | |||
ADDQ R13,R14 | |||
SHLQ $13,BP:BX | |||
SHLQ $13,BX,BP | |||
ANDQ SI,BX | |||
ADDQ R15,BX | |||
IMUL3Q $19,BP,DX |
@@ -87,18 +87,18 @@ TEXT ·square(SB),7,$0-16 | |||
ADDQ AX,R13 | |||
ADCQ DX,R14 | |||
MOVQ $REDMASK51,SI | |||
SHLQ $13,R8:CX | |||
SHLQ $13,CX,R8 | |||
ANDQ SI,CX | |||
SHLQ $13,R10:R9 | |||
SHLQ $13,R9,R10 | |||
ANDQ SI,R9 | |||
ADDQ R8,R9 | |||
SHLQ $13,R12:R11 | |||
SHLQ $13,R11,R12 | |||
ANDQ SI,R11 | |||
ADDQ R10,R11 | |||
SHLQ $13,R14:R13 | |||
SHLQ $13,R13,R14 | |||
ANDQ SI,R13 | |||
ADDQ R12,R13 | |||
SHLQ $13,BX:R15 | |||
SHLQ $13,R15,BX | |||
ANDQ SI,R15 | |||
ADDQ R14,R15 | |||
IMUL3Q $19,BX,DX |
@@ -0,0 +1,308 @@ | |||
// Copyright 2018 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.11 | |||
// +build !gccgo,!appengine | |||
#include "textflag.h" | |||
#define NUM_ROUNDS 10 | |||
// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) | |||
TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0 | |||
MOVD dst+0(FP), R1 | |||
MOVD src+24(FP), R2 | |||
MOVD src_len+32(FP), R3 | |||
MOVD key+48(FP), R4 | |||
MOVD nonce+56(FP), R6 | |||
MOVD counter+64(FP), R7 | |||
MOVD $·constants(SB), R10 | |||
MOVD $·incRotMatrix(SB), R11 | |||
MOVW (R7), R20 | |||
AND $~255, R3, R13 | |||
ADD R2, R13, R12 // R12 for block end | |||
AND $255, R3, R13 | |||
loop: | |||
MOVD $NUM_ROUNDS, R21 | |||
VLD1 (R11), [V30.S4, V31.S4] | |||
// load contants | |||
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4] | |||
WORD $0x4D60E940 | |||
// load keys | |||
// VLD4R 16(R4), [V4.S4, V5.S4, V6.S4, V7.S4] | |||
WORD $0x4DFFE884 | |||
// VLD4R 16(R4), [V8.S4, V9.S4, V10.S4, V11.S4] | |||
WORD $0x4DFFE888 | |||
SUB $32, R4 | |||
// load counter + nonce | |||
// VLD1R (R7), [V12.S4] | |||
WORD $0x4D40C8EC | |||
// VLD3R (R6), [V13.S4, V14.S4, V15.S4] | |||
WORD $0x4D40E8CD | |||
// update counter | |||
VADD V30.S4, V12.S4, V12.S4 | |||
chacha: | |||
// V0..V3 += V4..V7 | |||
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 16) | |||
VADD V0.S4, V4.S4, V0.S4 | |||
VADD V1.S4, V5.S4, V1.S4 | |||
VADD V2.S4, V6.S4, V2.S4 | |||
VADD V3.S4, V7.S4, V3.S4 | |||
VEOR V12.B16, V0.B16, V12.B16 | |||
VEOR V13.B16, V1.B16, V13.B16 | |||
VEOR V14.B16, V2.B16, V14.B16 | |||
VEOR V15.B16, V3.B16, V15.B16 | |||
VREV32 V12.H8, V12.H8 | |||
VREV32 V13.H8, V13.H8 | |||
VREV32 V14.H8, V14.H8 | |||
VREV32 V15.H8, V15.H8 | |||
// V8..V11 += V12..V15 | |||
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 12) | |||
VADD V8.S4, V12.S4, V8.S4 | |||
VADD V9.S4, V13.S4, V9.S4 | |||
VADD V10.S4, V14.S4, V10.S4 | |||
VADD V11.S4, V15.S4, V11.S4 | |||
VEOR V8.B16, V4.B16, V16.B16 | |||
VEOR V9.B16, V5.B16, V17.B16 | |||
VEOR V10.B16, V6.B16, V18.B16 | |||
VEOR V11.B16, V7.B16, V19.B16 | |||
VSHL $12, V16.S4, V4.S4 | |||
VSHL $12, V17.S4, V5.S4 | |||
VSHL $12, V18.S4, V6.S4 | |||
VSHL $12, V19.S4, V7.S4 | |||
VSRI $20, V16.S4, V4.S4 | |||
VSRI $20, V17.S4, V5.S4 | |||
VSRI $20, V18.S4, V6.S4 | |||
VSRI $20, V19.S4, V7.S4 | |||
// V0..V3 += V4..V7 | |||
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 8) | |||
VADD V0.S4, V4.S4, V0.S4 | |||
VADD V1.S4, V5.S4, V1.S4 | |||
VADD V2.S4, V6.S4, V2.S4 | |||
VADD V3.S4, V7.S4, V3.S4 | |||
VEOR V12.B16, V0.B16, V12.B16 | |||
VEOR V13.B16, V1.B16, V13.B16 | |||
VEOR V14.B16, V2.B16, V14.B16 | |||
VEOR V15.B16, V3.B16, V15.B16 | |||
VTBL V31.B16, [V12.B16], V12.B16 | |||
VTBL V31.B16, [V13.B16], V13.B16 | |||
VTBL V31.B16, [V14.B16], V14.B16 | |||
VTBL V31.B16, [V15.B16], V15.B16 | |||
// V8..V11 += V12..V15 | |||
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 7) | |||
VADD V12.S4, V8.S4, V8.S4 | |||
VADD V13.S4, V9.S4, V9.S4 | |||
VADD V14.S4, V10.S4, V10.S4 | |||
VADD V15.S4, V11.S4, V11.S4 | |||
VEOR V8.B16, V4.B16, V16.B16 | |||
VEOR V9.B16, V5.B16, V17.B16 | |||
VEOR V10.B16, V6.B16, V18.B16 | |||
VEOR V11.B16, V7.B16, V19.B16 | |||
VSHL $7, V16.S4, V4.S4 | |||
VSHL $7, V17.S4, V5.S4 | |||
VSHL $7, V18.S4, V6.S4 | |||
VSHL $7, V19.S4, V7.S4 | |||
VSRI $25, V16.S4, V4.S4 | |||
VSRI $25, V17.S4, V5.S4 | |||
VSRI $25, V18.S4, V6.S4 | |||
VSRI $25, V19.S4, V7.S4 | |||
// V0..V3 += V5..V7, V4 | |||
// V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16) | |||
VADD V0.S4, V5.S4, V0.S4 | |||
VADD V1.S4, V6.S4, V1.S4 | |||
VADD V2.S4, V7.S4, V2.S4 | |||
VADD V3.S4, V4.S4, V3.S4 | |||
VEOR V15.B16, V0.B16, V15.B16 | |||
VEOR V12.B16, V1.B16, V12.B16 | |||
VEOR V13.B16, V2.B16, V13.B16 | |||
VEOR V14.B16, V3.B16, V14.B16 | |||
VREV32 V12.H8, V12.H8 | |||
VREV32 V13.H8, V13.H8 | |||
VREV32 V14.H8, V14.H8 | |||
VREV32 V15.H8, V15.H8 | |||
// V10 += V15; V5 <<<= ((V10 XOR V5), 12) | |||
// ... | |||
VADD V15.S4, V10.S4, V10.S4 | |||
VADD V12.S4, V11.S4, V11.S4 | |||
VADD V13.S4, V8.S4, V8.S4 | |||
VADD V14.S4, V9.S4, V9.S4 | |||
VEOR V10.B16, V5.B16, V16.B16 | |||
VEOR V11.B16, V6.B16, V17.B16 | |||
VEOR V8.B16, V7.B16, V18.B16 | |||
VEOR V9.B16, V4.B16, V19.B16 | |||
VSHL $12, V16.S4, V5.S4 | |||
VSHL $12, V17.S4, V6.S4 | |||
VSHL $12, V18.S4, V7.S4 | |||
VSHL $12, V19.S4, V4.S4 | |||
VSRI $20, V16.S4, V5.S4 | |||
VSRI $20, V17.S4, V6.S4 | |||
VSRI $20, V18.S4, V7.S4 | |||
VSRI $20, V19.S4, V4.S4 | |||
// V0 += V5; V15 <<<= ((V0 XOR V15), 8) | |||
// ... | |||
VADD V5.S4, V0.S4, V0.S4 | |||
VADD V6.S4, V1.S4, V1.S4 | |||
VADD V7.S4, V2.S4, V2.S4 | |||
VADD V4.S4, V3.S4, V3.S4 | |||
VEOR V0.B16, V15.B16, V15.B16 | |||
VEOR V1.B16, V12.B16, V12.B16 | |||
VEOR V2.B16, V13.B16, V13.B16 | |||
VEOR V3.B16, V14.B16, V14.B16 | |||
VTBL V31.B16, [V12.B16], V12.B16 | |||
VTBL V31.B16, [V13.B16], V13.B16 | |||
VTBL V31.B16, [V14.B16], V14.B16 | |||
VTBL V31.B16, [V15.B16], V15.B16 | |||
// V10 += V15; V5 <<<= ((V10 XOR V5), 7) | |||
// ... | |||
VADD V15.S4, V10.S4, V10.S4 | |||
VADD V12.S4, V11.S4, V11.S4 | |||
VADD V13.S4, V8.S4, V8.S4 | |||
VADD V14.S4, V9.S4, V9.S4 | |||
VEOR V10.B16, V5.B16, V16.B16 | |||
VEOR V11.B16, V6.B16, V17.B16 | |||
VEOR V8.B16, V7.B16, V18.B16 | |||
VEOR V9.B16, V4.B16, V19.B16 | |||
VSHL $7, V16.S4, V5.S4 | |||
VSHL $7, V17.S4, V6.S4 | |||
VSHL $7, V18.S4, V7.S4 | |||
VSHL $7, V19.S4, V4.S4 | |||
VSRI $25, V16.S4, V5.S4 | |||
VSRI $25, V17.S4, V6.S4 | |||
VSRI $25, V18.S4, V7.S4 | |||
VSRI $25, V19.S4, V4.S4 | |||
SUB $1, R21 | |||
CBNZ R21, chacha | |||
// VLD4R (R10), [V16.S4, V17.S4, V18.S4, V19.S4] | |||
WORD $0x4D60E950 | |||
// VLD4R 16(R4), [V20.S4, V21.S4, V22.S4, V23.S4] | |||
WORD $0x4DFFE894 | |||
VADD V30.S4, V12.S4, V12.S4 | |||
VADD V16.S4, V0.S4, V0.S4 | |||
VADD V17.S4, V1.S4, V1.S4 | |||
VADD V18.S4, V2.S4, V2.S4 | |||
VADD V19.S4, V3.S4, V3.S4 | |||
// VLD4R 16(R4), [V24.S4, V25.S4, V26.S4, V27.S4] | |||
WORD $0x4DFFE898 | |||
// restore R4 | |||
SUB $32, R4 | |||
// load counter + nonce | |||
// VLD1R (R7), [V28.S4] | |||
WORD $0x4D40C8FC | |||
// VLD3R (R6), [V29.S4, V30.S4, V31.S4] | |||
WORD $0x4D40E8DD | |||
VADD V20.S4, V4.S4, V4.S4 | |||
VADD V21.S4, V5.S4, V5.S4 | |||
VADD V22.S4, V6.S4, V6.S4 | |||
VADD V23.S4, V7.S4, V7.S4 | |||
VADD V24.S4, V8.S4, V8.S4 | |||
VADD V25.S4, V9.S4, V9.S4 | |||
VADD V26.S4, V10.S4, V10.S4 | |||
VADD V27.S4, V11.S4, V11.S4 | |||
VADD V28.S4, V12.S4, V12.S4 | |||
VADD V29.S4, V13.S4, V13.S4 | |||
VADD V30.S4, V14.S4, V14.S4 | |||
VADD V31.S4, V15.S4, V15.S4 | |||
VZIP1 V1.S4, V0.S4, V16.S4 | |||
VZIP2 V1.S4, V0.S4, V17.S4 | |||
VZIP1 V3.S4, V2.S4, V18.S4 | |||
VZIP2 V3.S4, V2.S4, V19.S4 | |||
VZIP1 V5.S4, V4.S4, V20.S4 | |||
VZIP2 V5.S4, V4.S4, V21.S4 | |||
VZIP1 V7.S4, V6.S4, V22.S4 | |||
VZIP2 V7.S4, V6.S4, V23.S4 | |||
VZIP1 V9.S4, V8.S4, V24.S4 | |||
VZIP2 V9.S4, V8.S4, V25.S4 | |||
VZIP1 V11.S4, V10.S4, V26.S4 | |||
VZIP2 V11.S4, V10.S4, V27.S4 | |||
VZIP1 V13.S4, V12.S4, V28.S4 | |||
VZIP2 V13.S4, V12.S4, V29.S4 | |||
VZIP1 V15.S4, V14.S4, V30.S4 | |||
VZIP2 V15.S4, V14.S4, V31.S4 | |||
VZIP1 V18.D2, V16.D2, V0.D2 | |||
VZIP2 V18.D2, V16.D2, V4.D2 | |||
VZIP1 V19.D2, V17.D2, V8.D2 | |||
VZIP2 V19.D2, V17.D2, V12.D2 | |||
VLD1.P 64(R2), [V16.B16, V17.B16, V18.B16, V19.B16] | |||
VZIP1 V22.D2, V20.D2, V1.D2 | |||
VZIP2 V22.D2, V20.D2, V5.D2 | |||
VZIP1 V23.D2, V21.D2, V9.D2 | |||
VZIP2 V23.D2, V21.D2, V13.D2 | |||
VLD1.P 64(R2), [V20.B16, V21.B16, V22.B16, V23.B16] | |||
VZIP1 V26.D2, V24.D2, V2.D2 | |||
VZIP2 V26.D2, V24.D2, V6.D2 | |||
VZIP1 V27.D2, V25.D2, V10.D2 | |||
VZIP2 V27.D2, V25.D2, V14.D2 | |||
VLD1.P 64(R2), [V24.B16, V25.B16, V26.B16, V27.B16] | |||
VZIP1 V30.D2, V28.D2, V3.D2 | |||
VZIP2 V30.D2, V28.D2, V7.D2 | |||
VZIP1 V31.D2, V29.D2, V11.D2 | |||
VZIP2 V31.D2, V29.D2, V15.D2 | |||
VLD1.P 64(R2), [V28.B16, V29.B16, V30.B16, V31.B16] | |||
VEOR V0.B16, V16.B16, V16.B16 | |||
VEOR V1.B16, V17.B16, V17.B16 | |||
VEOR V2.B16, V18.B16, V18.B16 | |||
VEOR V3.B16, V19.B16, V19.B16 | |||
VST1.P [V16.B16, V17.B16, V18.B16, V19.B16], 64(R1) | |||
VEOR V4.B16, V20.B16, V20.B16 | |||
VEOR V5.B16, V21.B16, V21.B16 | |||
VEOR V6.B16, V22.B16, V22.B16 | |||
VEOR V7.B16, V23.B16, V23.B16 | |||
VST1.P [V20.B16, V21.B16, V22.B16, V23.B16], 64(R1) | |||
VEOR V8.B16, V24.B16, V24.B16 | |||
VEOR V9.B16, V25.B16, V25.B16 | |||
VEOR V10.B16, V26.B16, V26.B16 | |||
VEOR V11.B16, V27.B16, V27.B16 | |||
VST1.P [V24.B16, V25.B16, V26.B16, V27.B16], 64(R1) | |||
VEOR V12.B16, V28.B16, V28.B16 | |||
VEOR V13.B16, V29.B16, V29.B16 | |||
VEOR V14.B16, V30.B16, V30.B16 | |||
VEOR V15.B16, V31.B16, V31.B16 | |||
VST1.P [V28.B16, V29.B16, V30.B16, V31.B16], 64(R1) | |||
ADD $4, R20 | |||
MOVW R20, (R7) // update counter | |||
CMP R2, R12 | |||
BGT loop | |||
RET | |||
DATA ·constants+0x00(SB)/4, $0x61707865 | |||
DATA ·constants+0x04(SB)/4, $0x3320646e | |||
DATA ·constants+0x08(SB)/4, $0x79622d32 | |||
DATA ·constants+0x0c(SB)/4, $0x6b206574 | |||
GLOBL ·constants(SB), NOPTR|RODATA, $32 | |||
DATA ·incRotMatrix+0x00(SB)/4, $0x00000000 | |||
DATA ·incRotMatrix+0x04(SB)/4, $0x00000001 | |||
DATA ·incRotMatrix+0x08(SB)/4, $0x00000002 | |||
DATA ·incRotMatrix+0x0c(SB)/4, $0x00000003 | |||
DATA ·incRotMatrix+0x10(SB)/4, $0x02010003 | |||
DATA ·incRotMatrix+0x14(SB)/4, $0x06050407 | |||
DATA ·incRotMatrix+0x18(SB)/4, $0x0A09080B | |||
DATA ·incRotMatrix+0x1c(SB)/4, $0x0E0D0C0F | |||
GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32 |
@@ -0,0 +1,31 @@ | |||
// Copyright 2018 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.11 | |||
// +build !gccgo | |||
package chacha20 | |||
const ( | |||
haveAsm = true | |||
bufSize = 256 | |||
) | |||
//go:noescape | |||
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) | |||
func (c *Cipher) xorKeyStreamAsm(dst, src []byte) { | |||
if len(src) >= bufSize { | |||
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter) | |||
} | |||
if len(src)%bufSize != 0 { | |||
i := len(src) - len(src)%bufSize | |||
c.buf = [bufSize]byte{} | |||
copy(c.buf[:], src[i:]) | |||
xorKeyStreamVX(c.buf[:], c.buf[:], &c.key, &c.nonce, &c.counter) | |||
c.len = bufSize - copy(dst[i:], c.buf[:len(src)%bufSize]) | |||
} | |||
} |
@@ -2,7 +2,7 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build !s390x gccgo appengine | |||
// +build !arm64,!s390x arm64,!go1.11 gccgo appengine | |||
package chacha20 | |||
@@ -6,14 +6,13 @@ | |||
package chacha20 | |||
var haveAsm = hasVectorFacility() | |||
import ( | |||
"golang.org/x/sys/cpu" | |||
) | |||
const bufSize = 256 | |||
var haveAsm = cpu.S390X.HasVX | |||
// hasVectorFacility reports whether the machine supports the vector | |||
// facility (vx). | |||
// Implementation in asm_s390x.s. | |||
func hasVectorFacility() bool | |||
const bufSize = 256 | |||
// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only | |||
// be called when the vector facility is available. |
@@ -258,26 +258,3 @@ tail: | |||
MOVD R8, R3 | |||
MOVD $0, R4 | |||
JMP continue | |||
// func hasVectorFacility() bool | |||
TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1 | |||
MOVD $x-24(SP), R1 | |||
XC $24, 0(R1), 0(R1) // clear the storage | |||
MOVD $2, R0 // R0 is the number of double words stored -1 | |||
WORD $0xB2B01000 // STFLE 0(R1) | |||
XOR R0, R0 // reset the value of R0 | |||
MOVBZ z-8(SP), R1 | |||
AND $0x40, R1 | |||
BEQ novector | |||
vectorinstalled: | |||
// check if the vector instruction has been enabled | |||
VLEIB $0, $0xF, V16 | |||
VLGVB $0, V16, R1 | |||
CMPBNE R1, $0xF, novector | |||
MOVB $1, ret+0(FP) // have vx | |||
RET | |||
novector: | |||
MOVB $0, ret+0(FP) // no vx | |||
RET |
@@ -3,6 +3,10 @@ | |||
// license that can be found in the LICENSE file. | |||
// Package md4 implements the MD4 hash algorithm as defined in RFC 1320. | |||
// | |||
// Deprecated: MD4 is cryptographically broken and should should only be used | |||
// where compatibility with legacy systems, not security, is the goal. Instead, | |||
// use a secure hash like SHA-256 (from crypto/sha256). | |||
package md4 // import "golang.org/x/crypto/md4" | |||
import ( |
@@ -333,7 +333,6 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) { | |||
return nil, errors.StructuralError("primary key cannot be used for signatures") | |||
} | |||
var current *Identity | |||
var revocations []*packet.Signature | |||
EachPacket: | |||
for { | |||
@@ -346,36 +345,8 @@ EachPacket: | |||
switch pkt := p.(type) { | |||
case *packet.UserId: | |||
// Make a new Identity object, that we might wind up throwing away. | |||
// We'll only add it if we get a valid self-signature over this | |||
// userID. | |||
current = new(Identity) | |||
current.Name = pkt.Id | |||
current.UserId = pkt | |||
for { | |||
p, err = packets.Next() | |||
if err == io.EOF { | |||
break EachPacket | |||
} else if err != nil { | |||
return nil, err | |||
} | |||
sig, ok := p.(*packet.Signature) | |||
if !ok { | |||
packets.Unread(p) | |||
continue EachPacket | |||
} | |||
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { | |||
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil { | |||
return nil, errors.StructuralError("user ID self-signature invalid: " + err.Error()) | |||
} | |||
current.SelfSignature = sig | |||
e.Identities[pkt.Id] = current | |||
} else { | |||
current.Signatures = append(current.Signatures, sig) | |||
} | |||
if err := addUserID(e, packets, pkt); err != nil { | |||
return nil, err | |||
} | |||
case *packet.Signature: | |||
if pkt.SigType == packet.SigTypeKeyRevocation { | |||
@@ -384,11 +355,9 @@ EachPacket: | |||
// TODO: RFC4880 5.2.1 permits signatures | |||
// directly on keys (eg. to bind additional | |||
// revocation keys). | |||
} else if current == nil { | |||
return nil, errors.StructuralError("signature packet found before user id packet") | |||
} else { | |||
current.Signatures = append(current.Signatures, pkt) | |||
} | |||
// Else, ignoring the signature as it does not follow anything | |||
// we would know to attach it to. | |||
case *packet.PrivateKey: | |||
if pkt.IsSubkey == false { | |||
packets.Unread(p) | |||
@@ -429,33 +398,105 @@ EachPacket: | |||
return e, nil | |||
} | |||
func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error { | |||
// Make a new Identity object, that we might wind up throwing away. | |||
// We'll only add it if we get a valid self-signature over this | |||
// userID. | |||
identity := new(Identity) | |||
identity.Name = pkt.Id | |||
identity.UserId = pkt | |||
for { | |||
p, err := packets.Next() | |||
if err == io.EOF { | |||
break | |||
} else if err != nil { | |||
return err | |||
} | |||
sig, ok := p.(*packet.Signature) | |||
if !ok { | |||
packets.Unread(p) | |||
break | |||
} | |||
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { | |||
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil { | |||
return errors.StructuralError("user ID self-signature invalid: " + err.Error()) | |||
} | |||
identity.SelfSignature = sig | |||
e.Identities[pkt.Id] = identity | |||
} else { | |||
identity.Signatures = append(identity.Signatures, sig) | |||
} | |||
} | |||
return nil | |||
} | |||
func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error { | |||
var subKey Subkey | |||
subKey.PublicKey = pub | |||
subKey.PrivateKey = priv | |||
p, err := packets.Next() | |||
if err == io.EOF { | |||
return io.ErrUnexpectedEOF | |||
} | |||
if err != nil { | |||
return errors.StructuralError("subkey signature invalid: " + err.Error()) | |||
for { | |||
p, err := packets.Next() | |||
if err == io.EOF { | |||
break | |||
} else if err != nil { | |||
return errors.StructuralError("subkey signature invalid: " + err.Error()) | |||
} | |||
sig, ok := p.(*packet.Signature) | |||
if !ok { | |||
packets.Unread(p) | |||
break | |||
} | |||
if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation { | |||
return errors.StructuralError("subkey signature with wrong type") | |||
} | |||
if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil { | |||
return errors.StructuralError("subkey signature invalid: " + err.Error()) | |||
} | |||
switch sig.SigType { | |||
case packet.SigTypeSubkeyRevocation: | |||
subKey.Sig = sig | |||
case packet.SigTypeSubkeyBinding: | |||
if shouldReplaceSubkeySig(subKey.Sig, sig) { | |||
subKey.Sig = sig | |||
} | |||
} | |||
} | |||
var ok bool | |||
subKey.Sig, ok = p.(*packet.Signature) | |||
if !ok { | |||
if subKey.Sig == nil { | |||
return errors.StructuralError("subkey packet not followed by signature") | |||
} | |||
if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation { | |||
return errors.StructuralError("subkey signature with wrong type") | |||
} | |||
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig) | |||
if err != nil { | |||
return errors.StructuralError("subkey signature invalid: " + err.Error()) | |||
} | |||
e.Subkeys = append(e.Subkeys, subKey) | |||
return nil | |||
} | |||
func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool { | |||
if potentialNewSig == nil { | |||
return false | |||
} | |||
if existingSig == nil { | |||
return true | |||
} | |||
if existingSig.SigType == packet.SigTypeSubkeyRevocation { | |||
return false // never override a revocation signature | |||
} | |||
return potentialNewSig.CreationTime.After(existingSig.CreationTime) | |||
} | |||
const defaultRSAKeyBits = 2048 | |||
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a |
@@ -404,14 +404,16 @@ const ( | |||
type PublicKeyAlgorithm uint8 | |||
const ( | |||
PubKeyAlgoRSA PublicKeyAlgorithm = 1 | |||
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 | |||
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 | |||
PubKeyAlgoElGamal PublicKeyAlgorithm = 16 | |||
PubKeyAlgoDSA PublicKeyAlgorithm = 17 | |||
PubKeyAlgoRSA PublicKeyAlgorithm = 1 | |||
PubKeyAlgoElGamal PublicKeyAlgorithm = 16 | |||
PubKeyAlgoDSA PublicKeyAlgorithm = 17 | |||
// RFC 6637, Section 5. | |||
PubKeyAlgoECDH PublicKeyAlgorithm = 18 | |||
PubKeyAlgoECDSA PublicKeyAlgorithm = 19 | |||
// Deprecated in RFC 4880, Section 13.5. Use key flags instead. | |||
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 | |||
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 | |||
) | |||
// CanEncrypt returns true if it's possible to encrypt a message to a public |
@@ -64,14 +64,19 @@ func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateK | |||
return pk | |||
} | |||
// NewSignerPrivateKey creates a sign-only PrivateKey from a crypto.Signer that | |||
// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that | |||
// implements RSA or ECDSA. | |||
func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey { | |||
pk := new(PrivateKey) | |||
// In general, the public Keys should be used as pointers. We still | |||
// type-switch on the values, for backwards-compatibility. | |||
switch pubkey := signer.Public().(type) { | |||
case *rsa.PublicKey: | |||
pk.PublicKey = *NewRSAPublicKey(currentTime, pubkey) | |||
case rsa.PublicKey: | |||
pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey) | |||
pk.PubKeyAlgo = PubKeyAlgoRSASignOnly | |||
case *ecdsa.PublicKey: | |||
pk.PublicKey = *NewECDSAPublicKey(currentTime, pubkey) | |||
case ecdsa.PublicKey: | |||
pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey) | |||
default: |
@@ -542,7 +542,7 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e | |||
r, s, err = ecdsa.Sign(config.Random(), pk, digest) | |||
} else { | |||
var b []byte | |||
b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, nil) | |||
b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) | |||
if err == nil { | |||
r, s, err = unwrapECDSASig(b) | |||
} |
@@ -80,7 +80,7 @@ func (uat *UserAttribute) Serialize(w io.Writer) (err error) { | |||
// ImageData returns zero or more byte slices, each containing | |||
// JPEG File Interchange Format (JFIF), for each photo in the | |||
// the user attribute packet. | |||
// user attribute packet. | |||
func (uat *UserAttribute) ImageData() (imageData [][]byte) { | |||
for _, sp := range uat.Contents { | |||
if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { |
@@ -271,6 +271,7 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint | |||
// These are the possible hash functions that we'll use for the signature. | |||
candidateHashes := []uint8{ | |||
hashToHashId(crypto.SHA256), | |||
hashToHashId(crypto.SHA384), | |||
hashToHashId(crypto.SHA512), | |||
hashToHashId(crypto.SHA1), | |||
hashToHashId(crypto.RIPEMD160), | |||
@@ -349,6 +350,7 @@ func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Con | |||
// These are the possible hash functions that we'll use for the signature. | |||
candidateHashes := []uint8{ | |||
hashToHashId(crypto.SHA256), | |||
hashToHashId(crypto.SHA384), | |||
hashToHashId(crypto.SHA512), | |||
hashToHashId(crypto.SHA1), | |||
hashToHashId(crypto.RIPEMD160), |
@@ -0,0 +1,11 @@ | |||
// Copyright 2018 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 !amd64 gccgo appengine | |||
package poly1305 | |||
type mac struct{ macGeneric } | |||
func newMAC(key *[32]byte) mac { return mac{newMACGeneric(key)} } |
@@ -2,21 +2,19 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
/* | |||
Package poly1305 implements Poly1305 one-time message authentication code as | |||
specified in https://cr.yp.to/mac/poly1305-20050329.pdf. | |||
Poly1305 is a fast, one-time authentication function. It is infeasible for an | |||
attacker to generate an authenticator for a message without the key. However, a | |||
key must only be used for a single message. Authenticating two different | |||
messages with the same key allows an attacker to forge authenticators for other | |||
messages with the same key. | |||
Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was | |||
used with a fixed key in order to generate one-time keys from an nonce. | |||
However, in this package AES isn't used and the one-time key is specified | |||
directly. | |||
*/ | |||
// Package poly1305 implements Poly1305 one-time message authentication code as | |||
// specified in https://cr.yp.to/mac/poly1305-20050329.pdf. | |||
// | |||
// Poly1305 is a fast, one-time authentication function. It is infeasible for an | |||
// attacker to generate an authenticator for a message without the key. However, a | |||
// key must only be used for a single message. Authenticating two different | |||
// messages with the same key allows an attacker to forge authenticators for other | |||
// messages with the same key. | |||
// | |||
// Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was | |||
// used with a fixed key in order to generate one-time keys from an nonce. | |||
// However, in this package AES isn't used and the one-time key is specified | |||
// directly. | |||
package poly1305 // import "golang.org/x/crypto/poly1305" | |||
import "crypto/subtle" | |||
@@ -31,3 +29,55 @@ func Verify(mac *[16]byte, m []byte, key *[32]byte) bool { | |||
Sum(&tmp, m, key) | |||
return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1 | |||
} | |||
// New returns a new MAC computing an authentication | |||
// tag of all data written to it with the given key. | |||
// This allows writing the message progressively instead | |||
// of passing it as a single slice. Common users should use | |||
// the Sum function instead. | |||
// | |||
// The key must be unique for each message, as authenticating | |||
// two different messages with the same key allows an attacker | |||
// to forge messages at will. | |||
func New(key *[32]byte) *MAC { | |||
return &MAC{ | |||
mac: newMAC(key), | |||
finalized: false, | |||
} | |||
} | |||
// MAC is an io.Writer computing an authentication tag | |||
// of the data written to it. | |||
// | |||
// MAC cannot be used like common hash.Hash implementations, | |||
// because using a poly1305 key twice breaks its security. | |||
// Therefore writing data to a running MAC after calling | |||
// Sum causes it to panic. | |||
type MAC struct { | |||
mac // platform-dependent implementation | |||
finalized bool | |||
} | |||
// Size returns the number of bytes Sum will return. | |||
func (h *MAC) Size() int { return TagSize } | |||
// Write adds more data to the running message authentication code. | |||
// It never returns an error. | |||
// | |||
// It must not be called after the first call of Sum. | |||
func (h *MAC) Write(p []byte) (n int, err error) { | |||
if h.finalized { | |||
panic("poly1305: write to MAC after Sum") | |||
} | |||
return h.mac.Write(p) | |||
} | |||
// Sum computes the authenticator of all data written to the | |||
// message authentication code. | |||
func (h *MAC) Sum(b []byte) []byte { | |||
var mac [TagSize]byte | |||
h.mac.Sum(&mac) | |||
h.finalized = true | |||
return append(b, mac[:]...) | |||
} |
@@ -6,17 +6,63 @@ | |||
package poly1305 | |||
// This function is implemented in sum_amd64.s | |||
//go:noescape | |||
func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte) | |||
func initialize(state *[7]uint64, key *[32]byte) | |||
//go:noescape | |||
func update(state *[7]uint64, msg []byte) | |||
//go:noescape | |||
func finalize(tag *[TagSize]byte, state *[7]uint64) | |||
// Sum generates an authenticator for m using a one-time key and puts the | |||
// 16-byte result into out. Authenticating two different messages with the same | |||
// key allows an attacker to forge messages at will. | |||
func Sum(out *[16]byte, m []byte, key *[32]byte) { | |||
var mPtr *byte | |||
if len(m) > 0 { | |||
mPtr = &m[0] | |||
h := newMAC(key) | |||
h.Write(m) | |||
h.Sum(out) | |||
} | |||
func newMAC(key *[32]byte) (h mac) { | |||
initialize(&h.state, key) | |||
return | |||
} | |||
type mac struct { | |||
state [7]uint64 // := uint64{ h0, h1, h2, r0, r1, pad0, pad1 } | |||
buffer [TagSize]byte | |||
offset int | |||
} | |||
func (h *mac) Write(p []byte) (n int, err error) { | |||
n = len(p) | |||
if h.offset > 0 { | |||
remaining := TagSize - h.offset | |||
if n < remaining { | |||
h.offset += copy(h.buffer[h.offset:], p) | |||
return n, nil | |||
} | |||
copy(h.buffer[h.offset:], p[:remaining]) | |||
p = p[remaining:] | |||
h.offset = 0 | |||
update(&h.state, h.buffer[:]) | |||
} | |||
if nn := len(p) - (len(p) % TagSize); nn > 0 { | |||
update(&h.state, p[:nn]) | |||
p = p[nn:] | |||
} | |||
if len(p) > 0 { | |||
h.offset += copy(h.buffer[h.offset:], p) | |||
} | |||
return n, nil | |||
} | |||
func (h *mac) Sum(out *[16]byte) { | |||
state := h.state | |||
if h.offset > 0 { | |||
update(&state, h.buffer[:h.offset]) | |||
} | |||
poly1305(out, mPtr, uint64(len(m)), key) | |||
finalize(out, &state) | |||
} |
@@ -58,20 +58,17 @@ DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF | |||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC | |||
GLOBL ·poly1305Mask<>(SB), RODATA, $16 | |||
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key) | |||
TEXT ·poly1305(SB), $0-32 | |||
MOVQ out+0(FP), DI | |||
MOVQ m+8(FP), SI | |||
MOVQ mlen+16(FP), R15 | |||
MOVQ key+24(FP), AX | |||
MOVQ 0(AX), R11 | |||
MOVQ 8(AX), R12 | |||
ANDQ ·poly1305Mask<>(SB), R11 // r0 | |||
ANDQ ·poly1305Mask<>+8(SB), R12 // r1 | |||
XORQ R8, R8 // h0 | |||
XORQ R9, R9 // h1 | |||
XORQ R10, R10 // h2 | |||
// func update(state *[7]uint64, msg []byte) | |||
TEXT ·update(SB), $0-32 | |||
MOVQ state+0(FP), DI | |||
MOVQ msg_base+8(FP), SI | |||
MOVQ msg_len+16(FP), R15 | |||
MOVQ 0(DI), R8 // h0 | |||
MOVQ 8(DI), R9 // h1 | |||
MOVQ 16(DI), R10 // h2 | |||
MOVQ 24(DI), R11 // r0 | |||
MOVQ 32(DI), R12 // r1 | |||
CMPQ R15, $16 | |||
JB bytes_between_0_and_15 | |||
@@ -109,16 +106,42 @@ flush_buffer: | |||
JMP multiply | |||
done: | |||
MOVQ R8, AX | |||
MOVQ R9, BX | |||
MOVQ R8, 0(DI) | |||
MOVQ R9, 8(DI) | |||
MOVQ R10, 16(DI) | |||
RET | |||
// func initialize(state *[7]uint64, key *[32]byte) | |||
TEXT ·initialize(SB), $0-16 | |||
MOVQ state+0(FP), DI | |||
MOVQ key+8(FP), SI | |||
// state[0...7] is initialized with zero | |||
MOVOU 0(SI), X0 | |||
MOVOU 16(SI), X1 | |||
MOVOU ·poly1305Mask<>(SB), X2 | |||
PAND X2, X0 | |||
MOVOU X0, 24(DI) | |||
MOVOU X1, 40(DI) | |||
RET | |||
// func finalize(tag *[TagSize]byte, state *[7]uint64) | |||
TEXT ·finalize(SB), $0-16 | |||
MOVQ tag+0(FP), DI | |||
MOVQ state+8(FP), SI | |||
MOVQ 0(SI), AX | |||
MOVQ 8(SI), BX | |||
MOVQ 16(SI), CX | |||
MOVQ AX, R8 | |||
MOVQ BX, R9 | |||
SUBQ $0xFFFFFFFFFFFFFFFB, AX | |||
SBBQ $0xFFFFFFFFFFFFFFFF, BX | |||
SBBQ $3, R10 | |||
SBBQ $3, CX | |||
CMOVQCS R8, AX | |||
CMOVQCS R9, BX | |||
MOVQ key+24(FP), R8 | |||
ADDQ 16(R8), AX | |||
ADCQ 24(R8), BX | |||
ADDQ 40(SI), AX | |||
ADCQ 48(SI), BX | |||
MOVQ AX, 0(DI) | |||
MOVQ BX, 8(DI) |
@@ -1,4 +1,4 @@ | |||
// Copyright 2012 The Go Authors. All rights reserved. | |||
// Copyright 2018 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. | |||
@@ -6,21 +6,79 @@ package poly1305 | |||
import "encoding/binary" | |||
const ( | |||
msgBlock = uint32(1 << 24) | |||
finalBlock = uint32(0) | |||
) | |||
// sumGeneric generates an authenticator for msg using a one-time key and | |||
// puts the 16-byte result into out. This is the generic implementation of | |||
// Sum and should be called if no assembly implementation is available. | |||
func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) { | |||
var ( | |||
h0, h1, h2, h3, h4 uint32 // the hash accumulators | |||
r0, r1, r2, r3, r4 uint64 // the r part of the key | |||
) | |||
h := newMACGeneric(key) | |||
h.Write(msg) | |||
h.Sum(out) | |||
} | |||
func newMACGeneric(key *[32]byte) (h macGeneric) { | |||
h.r[0] = binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff | |||
h.r[1] = (binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03 | |||
h.r[2] = (binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff | |||
h.r[3] = (binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff | |||
h.r[4] = (binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff | |||
h.s[0] = binary.LittleEndian.Uint32(key[16:]) | |||
h.s[1] = binary.LittleEndian.Uint32(key[20:]) | |||
h.s[2] = binary.LittleEndian.Uint32(key[24:]) | |||
h.s[3] = binary.LittleEndian.Uint32(key[28:]) | |||
return | |||
} | |||
type macGeneric struct { | |||
h, r [5]uint32 | |||
s [4]uint32 | |||
buffer [TagSize]byte | |||
offset int | |||
} | |||
func (h *macGeneric) Write(p []byte) (n int, err error) { | |||
n = len(p) | |||
if h.offset > 0 { | |||
remaining := TagSize - h.offset | |||
if n < remaining { | |||
h.offset += copy(h.buffer[h.offset:], p) | |||
return n, nil | |||
} | |||
copy(h.buffer[h.offset:], p[:remaining]) | |||
p = p[remaining:] | |||
h.offset = 0 | |||
updateGeneric(h.buffer[:], msgBlock, &(h.h), &(h.r)) | |||
} | |||
if nn := len(p) - (len(p) % TagSize); nn > 0 { | |||
updateGeneric(p, msgBlock, &(h.h), &(h.r)) | |||
p = p[nn:] | |||
} | |||
if len(p) > 0 { | |||
h.offset += copy(h.buffer[h.offset:], p) | |||
} | |||
return n, nil | |||
} | |||
r0 = uint64(binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff) | |||
r1 = uint64((binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03) | |||
r2 = uint64((binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff) | |||
r3 = uint64((binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff) | |||
r4 = uint64((binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff) | |||
func (h *macGeneric) Sum(out *[16]byte) { | |||
H, R := h.h, h.r | |||
if h.offset > 0 { | |||
var buffer [TagSize]byte | |||
copy(buffer[:], h.buffer[:h.offset]) | |||
buffer[h.offset] = 1 // invariant: h.offset < TagSize | |||
updateGeneric(buffer[:], finalBlock, &H, &R) | |||
} | |||
finalizeGeneric(out, &H, &(h.s)) | |||
} | |||
func updateGeneric(msg []byte, flag uint32, h, r *[5]uint32) { | |||
h0, h1, h2, h3, h4 := h[0], h[1], h[2], h[3], h[4] | |||
r0, r1, r2, r3, r4 := uint64(r[0]), uint64(r[1]), uint64(r[2]), uint64(r[3]), uint64(r[4]) | |||
R1, R2, R3, R4 := r1*5, r2*5, r3*5, r4*5 | |||
for len(msg) >= TagSize { | |||
@@ -29,7 +87,7 @@ func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) { | |||
h1 += (binary.LittleEndian.Uint32(msg[3:]) >> 2) & 0x3ffffff | |||
h2 += (binary.LittleEndian.Uint32(msg[6:]) >> 4) & 0x3ffffff | |||
h3 += (binary.LittleEndian.Uint32(msg[9:]) >> 6) & 0x3ffffff | |||
h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | (1 << 24) | |||
h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | flag | |||
// h *= r | |||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1) | |||
@@ -52,36 +110,11 @@ func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) { | |||
msg = msg[TagSize:] | |||
} | |||
if len(msg) > 0 { | |||
var block [TagSize]byte | |||
off := copy(block[:], msg) | |||
block[off] = 0x01 | |||
// h += msg | |||
h0 += binary.LittleEndian.Uint32(block[0:]) & 0x3ffffff | |||
h1 += (binary.LittleEndian.Uint32(block[3:]) >> 2) & 0x3ffffff | |||
h2 += (binary.LittleEndian.Uint32(block[6:]) >> 4) & 0x3ffffff | |||
h3 += (binary.LittleEndian.Uint32(block[9:]) >> 6) & 0x3ffffff | |||
h4 += (binary.LittleEndian.Uint32(block[12:]) >> 8) | |||
// h *= r | |||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1) | |||
d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2) | |||
d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3) | |||
d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4) | |||
d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0) | |||
// h %= p | |||
h0 = uint32(d0) & 0x3ffffff | |||
h1 = uint32(d1) & 0x3ffffff | |||
h2 = uint32(d2) & 0x3ffffff | |||
h3 = uint32(d3) & 0x3ffffff | |||
h4 = uint32(d4) & 0x3ffffff | |||
h[0], h[1], h[2], h[3], h[4] = h0, h1, h2, h3, h4 | |||
} | |||
h0 += uint32(d4>>26) * 5 | |||
h1 += h0 >> 26 | |||
h0 = h0 & 0x3ffffff | |||
} | |||
func finalizeGeneric(out *[TagSize]byte, h *[5]uint32, s *[4]uint32) { | |||
h0, h1, h2, h3, h4 := h[0], h[1], h[2], h[3], h[4] | |||
// h %= p reduction | |||
h2 += h1 >> 26 | |||
@@ -123,13 +156,13 @@ func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) { | |||
// s: the s part of the key | |||
// tag = (h + s) % (2^128) | |||
t := uint64(h0) + uint64(binary.LittleEndian.Uint32(key[16:])) | |||
t := uint64(h0) + uint64(s[0]) | |||
h0 = uint32(t) | |||
t = uint64(h1) + uint64(binary.LittleEndian.Uint32(key[20:])) + (t >> 32) | |||
t = uint64(h1) + uint64(s[1]) + (t >> 32) | |||
h1 = uint32(t) | |||
t = uint64(h2) + uint64(binary.LittleEndian.Uint32(key[24:])) + (t >> 32) | |||
t = uint64(h2) + uint64(s[2]) + (t >> 32) | |||
h2 = uint32(t) | |||
t = uint64(h3) + uint64(binary.LittleEndian.Uint32(key[28:])) + (t >> 32) | |||
t = uint64(h3) + uint64(s[3]) + (t >> 32) | |||
h3 = uint32(t) | |||
binary.LittleEndian.PutUint32(out[0:], h0) |
@@ -10,5 +10,7 @@ package poly1305 | |||
// 16-byte result into out. Authenticating two different messages with the same | |||
// key allows an attacker to forge messages at will. | |||
func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) { | |||
sumGeneric(out, msg, key) | |||
h := newMAC(key) | |||
h.Write(msg) | |||
h.Sum(out) | |||
} |
@@ -6,16 +6,9 @@ | |||
package poly1305 | |||
// hasVectorFacility reports whether the machine supports | |||
// the vector facility (vx). | |||
func hasVectorFacility() bool | |||
// hasVMSLFacility reports whether the machine supports | |||
// Vector Multiply Sum Logical (VMSL). | |||
func hasVMSLFacility() bool | |||
var hasVX = hasVectorFacility() | |||
var hasVMSL = hasVMSLFacility() | |||
import ( | |||
"golang.org/x/sys/cpu" | |||
) | |||
// poly1305vx is an assembly implementation of Poly1305 that uses vector | |||
// instructions. It must only be called if the vector facility (vx) is | |||
@@ -33,12 +26,12 @@ func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]byte) | |||
// 16-byte result into out. Authenticating two different messages with the same | |||
// key allows an attacker to forge messages at will. | |||
func Sum(out *[16]byte, m []byte, key *[32]byte) { | |||
if hasVX { | |||
if cpu.S390X.HasVX { | |||
var mPtr *byte | |||
if len(m) > 0 { | |||
mPtr = &m[0] | |||
} | |||
if hasVMSL && len(m) > 256 { | |||
if cpu.S390X.HasVXE && len(m) > 256 { | |||
poly1305vmsl(out, mPtr, uint64(len(m)), key) | |||
} else { | |||
poly1305vx(out, mPtr, uint64(len(m)), key) |
@@ -376,25 +376,3 @@ b1: | |||
MOVD $0, R3 | |||
BR multiply | |||
TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1 | |||
MOVD $x-24(SP), R1 | |||
XC $24, 0(R1), 0(R1) // clear the storage | |||
MOVD $2, R0 // R0 is the number of double words stored -1 | |||
WORD $0xB2B01000 // STFLE 0(R1) | |||
XOR R0, R0 // reset the value of R0 | |||
MOVBZ z-8(SP), R1 | |||
AND $0x40, R1 | |||
BEQ novector | |||
vectorinstalled: | |||
// check if the vector instruction has been enabled | |||
VLEIB $0, $0xF, V16 | |||
VLGVB $0, V16, R1 | |||
CMPBNE R1, $0xF, novector | |||
MOVB $1, ret+0(FP) // have vx | |||
RET | |||
novector: | |||
MOVB $0, ret+0(FP) // no vx | |||
RET |
@@ -907,25 +907,3 @@ square: | |||
MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) | |||
REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5) | |||
BR next | |||
TEXT ·hasVMSLFacility(SB), NOSPLIT, $24-1 | |||
MOVD $x-24(SP), R1 | |||
XC $24, 0(R1), 0(R1) // clear the storage | |||
MOVD $2, R0 // R0 is the number of double words stored -1 | |||
WORD $0xB2B01000 // STFLE 0(R1) | |||
XOR R0, R0 // reset the value of R0 | |||
MOVBZ z-8(SP), R1 | |||
AND $0x01, R1 | |||
BEQ novmsl | |||
vectorinstalled: | |||
// check if the vector instruction has been enabled | |||
VLEIB $0, $0xF, V16 | |||
VLGVB $0, V16, R1 | |||
CMPBNE R1, $0xF, novmsl | |||
MOVB $1, ret+0(FP) // have vx | |||
RET | |||
novmsl: | |||
MOVB $0, ret+0(FP) // no vx | |||
RET |
@@ -25,10 +25,22 @@ import ( | |||
"math/big" | |||
"sync" | |||
"crypto" | |||
"golang.org/x/crypto/ed25519" | |||
"golang.org/x/crypto/ssh" | |||
) | |||
// SignatureFlags represent additional flags that can be passed to the signature | |||
// requests an defined in [PROTOCOL.agent] section 4.5.1. | |||
type SignatureFlags uint32 | |||
// SignatureFlag values as defined in [PROTOCOL.agent] section 5.3. | |||
const ( | |||
SignatureFlagReserved SignatureFlags = 1 << iota | |||
SignatureFlagRsaSha256 | |||
SignatureFlagRsaSha512 | |||
) | |||
// Agent represents the capabilities of an ssh-agent. | |||
type Agent interface { | |||
// List returns the identities known to the agent. | |||
@@ -57,6 +69,26 @@ type Agent interface { | |||
Signers() ([]ssh.Signer, error) | |||
} | |||
type ExtendedAgent interface { | |||
Agent | |||
// SignWithFlags signs like Sign, but allows for additional flags to be sent/received | |||
SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) | |||
// Extension processes a custom extension request. Standard-compliant agents are not | |||
// required to support any extensions, but this method allows agents to implement | |||
// vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 4.7. | |||
// If agent extensions are unsupported entirely this method MUST return an | |||
// ErrExtensionUnsupported error. Similarly, if just the specific extensionType in | |||
// the request is unsupported by the agent then ErrExtensionUnsupported MUST be | |||
// returned. | |||
// | |||
// In the case of success, since [PROTOCOL.agent] section 4.7 specifies that the contents | |||
// of the response are unspecified (including the type of the message), the complete | |||
// response will be returned as a []byte slice, including the "type" byte of the message. | |||
Extension(extensionType string, contents []byte) ([]byte, error) | |||
} | |||
// ConstraintExtension describes an optional constraint defined by users. | |||
type ConstraintExtension struct { | |||
// ExtensionName consist of a UTF-8 string suffixed by the | |||
@@ -179,6 +211,23 @@ type constrainExtensionAgentMsg struct { | |||
Rest []byte `ssh:"rest"` | |||
} | |||
// See [PROTOCOL.agent], section 4.7 | |||
const agentExtension = 27 | |||
const agentExtensionFailure = 28 | |||
// ErrExtensionUnsupported indicates that an extension defined in | |||
// [PROTOCOL.agent] section 4.7 is unsupported by the agent. Specifically this | |||
// error indicates that the agent returned a standard SSH_AGENT_FAILURE message | |||
// as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol | |||
// specification (and therefore this error) does not distinguish between a | |||
// specific extension being unsupported and extensions being unsupported entirely. | |||
var ErrExtensionUnsupported = errors.New("agent: extension unsupported") | |||
type extensionAgentMsg struct { | |||
ExtensionType string `sshtype:"27"` | |||
Contents []byte | |||
} | |||
// Key represents a protocol 2 public key as defined in | |||
// [PROTOCOL.agent], section 2.5.2. | |||
type Key struct { | |||
@@ -260,7 +309,7 @@ type client struct { | |||
// NewClient returns an Agent that talks to an ssh-agent process over | |||
// the given connection. | |||
func NewClient(rw io.ReadWriter) Agent { | |||
func NewClient(rw io.ReadWriter) ExtendedAgent { | |||
return &client{conn: rw} | |||
} | |||
@@ -268,6 +317,21 @@ func NewClient(rw io.ReadWriter) Agent { | |||
// unmarshaled into reply and replyType is set to the first byte of | |||
// the reply, which contains the type of the message. | |||
func (c *client) call(req []byte) (reply interface{}, err error) { | |||
buf, err := c.callRaw(req) | |||
if err != nil { | |||
return nil, err | |||
} | |||
reply, err = unmarshal(buf) | |||
if err != nil { | |||
return nil, clientErr(err) | |||
} | |||
return reply, nil | |||
} | |||
// callRaw sends an RPC to the agent. On success, the raw | |||
// bytes of the response are returned; no unmarshalling is | |||
// performed on the response. | |||
func (c *client) callRaw(req []byte) (reply []byte, err error) { | |||
c.mu.Lock() | |||
defer c.mu.Unlock() | |||
@@ -284,18 +348,14 @@ func (c *client) call(req []byte) (reply interface{}, err error) { | |||
} | |||
respSize := binary.BigEndian.Uint32(respSizeBuf[:]) | |||
if respSize > maxAgentResponseBytes { | |||
return nil, clientErr(err) | |||
return nil, clientErr(errors.New("response too large")) | |||
} | |||
buf := make([]byte, respSize) | |||
if _, err = io.ReadFull(c.conn, buf); err != nil { | |||
return nil, clientErr(err) | |||
} | |||
reply, err = unmarshal(buf) | |||
if err != nil { | |||
return nil, clientErr(err) | |||
} | |||
return reply, err | |||
return buf, nil | |||
} | |||
func (c *client) simpleCall(req []byte) error { | |||
@@ -369,9 +429,14 @@ func (c *client) List() ([]*Key, error) { | |||
// Sign has the agent sign the data using a protocol 2 key as defined | |||
// in [PROTOCOL.agent] section 2.6.2. | |||
func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { | |||
return c.SignWithFlags(key, data, 0) | |||
} | |||
func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) { | |||
req := ssh.Marshal(signRequestAgentMsg{ | |||
KeyBlob: key.Marshal(), | |||
Data: data, | |||
Flags: uint32(flags), | |||
}) | |||
msg, err := c.call(req) | |||
@@ -681,3 +746,44 @@ func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, | |||
// The agent has its own entropy source, so the rand argument is ignored. | |||
return s.agent.Sign(s.pub, data) | |||
} | |||
func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) { | |||
var flags SignatureFlags | |||
if opts != nil { | |||
switch opts.HashFunc() { | |||
case crypto.SHA256: | |||
flags = SignatureFlagRsaSha256 | |||
case crypto.SHA512: | |||
flags = SignatureFlagRsaSha512 | |||
} | |||
} | |||
return s.agent.SignWithFlags(s.pub, data, flags) | |||
} | |||
// Calls an extension method. It is up to the agent implementation as to whether or not | |||
// any particular extension is supported and may always return an error. Because the | |||
// type of the response is up to the implementation, this returns the bytes of the | |||
// response and does not attempt any type of unmarshalling. | |||
func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) { | |||
req := ssh.Marshal(extensionAgentMsg{ | |||
ExtensionType: extensionType, | |||
Contents: contents, | |||
}) | |||
buf, err := c.callRaw(req) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if len(buf) == 0 { | |||
return nil, errors.New("agent: failure; empty response") | |||
} | |||
// [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message | |||
// represents an agent that does not support the extension | |||
if buf[0] == agentFailure { | |||
return nil, ErrExtensionUnsupported | |||
} | |||
if buf[0] == agentExtensionFailure { | |||
return nil, errors.New("agent: generic extension failure") | |||
} | |||
return buf, nil | |||
} |
@@ -182,6 +182,10 @@ func (r *keyring) Add(key AddedKey) error { | |||
// Sign returns a signature for the data. | |||
func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { | |||
return r.SignWithFlags(key, data, 0) | |||
} | |||
func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) { | |||
r.mu.Lock() | |||
defer r.mu.Unlock() | |||
if r.locked { | |||
@@ -192,7 +196,24 @@ func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { | |||
wanted := key.Marshal() | |||
for _, k := range r.keys { | |||
if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) { | |||
return k.signer.Sign(rand.Reader, data) | |||
if flags == 0 { | |||
return k.signer.Sign(rand.Reader, data) | |||
} else { | |||
if algorithmSigner, ok := k.signer.(ssh.AlgorithmSigner); !ok { | |||
return nil, fmt.Errorf("agent: signature does not support non-default signature algorithm: %T", k.signer) | |||
} else { | |||
var algorithm string | |||
switch flags { | |||
case SignatureFlagRsaSha256: | |||
algorithm = ssh.SigAlgoRSASHA2256 | |||
case SignatureFlagRsaSha512: | |||
algorithm = ssh.SigAlgoRSASHA2512 | |||
default: | |||
return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags) | |||
} | |||
return algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm) | |||
} | |||
} | |||
} | |||
} | |||
return nil, errors.New("not found") | |||
@@ -213,3 +234,8 @@ func (r *keyring) Signers() ([]ssh.Signer, error) { | |||
} | |||
return s, nil | |||
} | |||
// The keyring does not support any extensions | |||
func (r *keyring) Extension(extensionType string, contents []byte) ([]byte, error) { | |||
return nil, ErrExtensionUnsupported | |||
} |
@@ -128,7 +128,14 @@ func (s *server) processRequest(data []byte) (interface{}, error) { | |||
Blob: req.KeyBlob, | |||
} | |||
sig, err := s.agent.Sign(k, req.Data) // TODO(hanwen): flags. | |||
var sig *ssh.Signature | |||
var err error | |||
if extendedAgent, ok := s.agent.(ExtendedAgent); ok { | |||
sig, err = extendedAgent.SignWithFlags(k, req.Data, SignatureFlags(req.Flags)) | |||
} else { | |||
sig, err = s.agent.Sign(k, req.Data) | |||
} | |||
if err != nil { | |||
return nil, err | |||
} | |||
@@ -150,6 +157,43 @@ func (s *server) processRequest(data []byte) (interface{}, error) { | |||
case agentAddIDConstrained, agentAddIdentity: | |||
return nil, s.insertIdentity(data) | |||
case agentExtension: | |||
// Return a stub object where the whole contents of the response gets marshaled. | |||
var responseStub struct { | |||
Rest []byte `ssh:"rest"` | |||
} | |||
if extendedAgent, ok := s.agent.(ExtendedAgent); !ok { | |||
// If this agent doesn't implement extensions, [PROTOCOL.agent] section 4.7 | |||
// requires that we return a standard SSH_AGENT_FAILURE message. | |||
responseStub.Rest = []byte{agentFailure} | |||
} else { | |||
var req extensionAgentMsg | |||
if err := ssh.Unmarshal(data, &req); err != nil { | |||
return nil, err | |||
} | |||
res, err := extendedAgent.Extension(req.ExtensionType, req.Contents) | |||
if err != nil { | |||
// If agent extensions are unsupported, return a standard SSH_AGENT_FAILURE | |||
// message as required by [PROTOCOL.agent] section 4.7. | |||
if err == ErrExtensionUnsupported { | |||
responseStub.Rest = []byte{agentFailure} | |||
} else { | |||
// As the result of any other error processing an extension request, | |||
// [PROTOCOL.agent] section 4.7 requires that we return a | |||
// SSH_AGENT_EXTENSION_FAILURE code. | |||
responseStub.Rest = []byte{agentExtensionFailure} | |||
} | |||
} else { | |||
if len(res) == 0 { | |||
return nil, nil | |||
} | |||
responseStub.Rest = res | |||
} | |||
} | |||
return responseStub, nil | |||
} | |||
return nil, fmt.Errorf("unknown opcode %d", data[0]) | |||
@@ -497,6 +541,9 @@ func ServeAgent(agent Agent, c io.ReadWriter) error { | |||
return err | |||
} | |||
l := binary.BigEndian.Uint32(length[:]) | |||
if l == 0 { | |||
return fmt.Errorf("agent: request size is 0") | |||
} | |||
if l > maxAgentResponseBytes { | |||
// We also cap requests. | |||
return fmt.Errorf("agent: request too large: %d", l) |
@@ -222,6 +222,11 @@ type openSSHCertSigner struct { | |||
signer Signer | |||
} | |||
type algorithmOpenSSHCertSigner struct { | |||
*openSSHCertSigner | |||
algorithmSigner AlgorithmSigner | |||
} | |||
// NewCertSigner returns a Signer that signs with the given Certificate, whose | |||
// private key is held by signer. It returns an error if the public key in cert | |||
// doesn't match the key used by signer. | |||
@@ -230,7 +235,12 @@ func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { | |||
return nil, errors.New("ssh: signer and cert have different public key") | |||
} | |||
return &openSSHCertSigner{cert, signer}, nil | |||
if algorithmSigner, ok := signer.(AlgorithmSigner); ok { | |||
return &algorithmOpenSSHCertSigner{ | |||
&openSSHCertSigner{cert, signer}, algorithmSigner}, nil | |||
} else { | |||
return &openSSHCertSigner{cert, signer}, nil | |||
} | |||
} | |||
func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { | |||
@@ -241,6 +251,10 @@ func (s *openSSHCertSigner) PublicKey() PublicKey { | |||
return s.pub | |||
} | |||
func (s *algorithmOpenSSHCertSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { | |||
return s.algorithmSigner.SignWithAlgorithm(rand, data, algorithm) | |||
} | |||
const sourceAddressCriticalOption = "source-address" | |||
// CertChecker does the work of verifying a certificate. Its methods |
@@ -149,8 +149,8 @@ type streamPacketCipher struct { | |||
macResult []byte | |||
} | |||
// readPacket reads and decrypt a single packet from the reader argument. | |||
func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
// readCipherPacket reads and decrypt a single packet from the reader argument. | |||
func (s *streamPacketCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
if _, err := io.ReadFull(r, s.prefix[:]); err != nil { | |||
return nil, err | |||
} | |||
@@ -221,8 +221,8 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err | |||
return s.packetData[:length-paddingLength-1], nil | |||
} | |||
// writePacket encrypts and sends a packet of data to the writer argument | |||
func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |||
// writeCipherPacket encrypts and sends a packet of data to the writer argument | |||
func (s *streamPacketCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |||
if len(packet) > maxPacket { | |||
return errors.New("ssh: packet too large") | |||
} | |||
@@ -327,7 +327,7 @@ func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) | |||
const gcmTagSize = 16 | |||
func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |||
func (c *gcmCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |||
// Pad out to multiple of 16 bytes. This is different from the | |||
// stream cipher because that encrypts the length too. | |||
padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) | |||
@@ -370,7 +370,7 @@ func (c *gcmCipher) incIV() { | |||
} | |||
} | |||
func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
func (c *gcmCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
if _, err := io.ReadFull(r, c.prefix[:]); err != nil { | |||
return nil, err | |||
} | |||
@@ -486,8 +486,8 @@ type cbcError string | |||
func (e cbcError) Error() string { return string(e) } | |||
func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
p, err := c.readPacketLeaky(seqNum, r) | |||
func (c *cbcCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
p, err := c.readCipherPacketLeaky(seqNum, r) | |||
if err != nil { | |||
if _, ok := err.(cbcError); ok { | |||
// Verification error: read a fixed amount of | |||
@@ -500,7 +500,7 @@ func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
return p, err | |||
} | |||
func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { | |||
func (c *cbcCipher) readCipherPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { | |||
blockSize := c.decrypter.BlockSize() | |||
// Read the header, which will include some of the subsequent data in the | |||
@@ -576,7 +576,7 @@ func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) | |||
return c.packetData[prefixLen:paddingStart], nil | |||
} | |||
func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |||
func (c *cbcCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |||
effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize()) | |||
// Length of encrypted portion of the packet (header, payload, padding). | |||
@@ -665,7 +665,7 @@ func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionA | |||
return c, nil | |||
} | |||
func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
func (c *chacha20Poly1305Cipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |||
nonce := [3]uint32{0, 0, bits.ReverseBytes32(seqNum)} | |||
s := chacha20.New(c.contentKey, nonce) | |||
var polyKey [32]byte | |||
@@ -723,7 +723,7 @@ func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte, | |||
return plain, nil | |||
} | |||
func (c *chacha20Poly1305Cipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error { | |||
func (c *chacha20Poly1305Cipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error { | |||
nonce := [3]uint32{0, 0, bits.ReverseBytes32(seqNum)} | |||
s := chacha20.New(c.contentKey, nonce) | |||
var polyKey [32]byte |
@@ -185,7 +185,7 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) { | |||
// 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. | |||
// net.Conn underlying the SSH connection. | |||
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error | |||
// BannerCallback is the function type used for treat the banner sent by |
@@ -109,6 +109,7 @@ func findCommon(what string, client []string, server []string) (common string, e | |||
return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server) | |||
} | |||
// directionAlgorithms records algorithm choices in one direction (either read or write) | |||
type directionAlgorithms struct { | |||
Cipher string | |||
MAC string | |||
@@ -137,7 +138,7 @@ type algorithms struct { | |||
r directionAlgorithms | |||
} | |||
func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { | |||
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { | |||
result := &algorithms{} | |||
result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos) | |||
@@ -150,32 +151,37 @@ func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algor | |||
return | |||
} | |||
result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) | |||
stoc, ctos := &result.w, &result.r | |||
if isClient { | |||
ctos, stoc = stoc, ctos | |||
} | |||
ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) | |||
if err != nil { | |||
return | |||
} | |||
result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) | |||
stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) | |||
if err != nil { | |||
return | |||
} | |||
result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) | |||
ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) | |||
if err != nil { | |||
return | |||
} | |||
result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) | |||
stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) | |||
if err != nil { | |||
return | |||
} | |||
result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) | |||
ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) | |||
if err != nil { | |||
return | |||
} | |||
result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) | |||
stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) | |||
if err != nil { | |||
return | |||
} |
@@ -543,7 +543,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { | |||
clientInit := otherInit | |||
serverInit := t.sentInitMsg | |||
if len(t.hostKeys) == 0 { | |||
isClient := len(t.hostKeys) == 0 | |||
if isClient { | |||
clientInit, serverInit = serverInit, clientInit | |||
magics.clientKexInit = t.sentInitPacket | |||
@@ -551,7 +552,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { | |||
} | |||
var err error | |||
t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit) | |||
t.algorithms, err = findAgreedAlgorithms(isClient, clientInit, serverInit) | |||
if err != nil { | |||
return err | |||
} |
@@ -38,6 +38,16 @@ const ( | |||
KeyAlgoED25519 = "ssh-ed25519" | |||
) | |||
// These constants represent non-default signature algorithms that are supported | |||
// as algorithm parameters to AlgorithmSigner.SignWithAlgorithm methods. See | |||
// [PROTOCOL.agent] section 4.5.1 and | |||
// https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-10 | |||
const ( | |||
SigAlgoRSA = "ssh-rsa" | |||
SigAlgoRSASHA2256 = "rsa-sha2-256" | |||
SigAlgoRSASHA2512 = "rsa-sha2-512" | |||
) | |||
// parsePubKey parses a public key of the given algorithm. | |||
// Use ParsePublicKey for keys with prepended algorithm. | |||
func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { | |||
@@ -301,6 +311,19 @@ type Signer interface { | |||
Sign(rand io.Reader, data []byte) (*Signature, error) | |||
} | |||
// A AlgorithmSigner is a Signer that also supports specifying a specific | |||
// algorithm to use for signing. | |||
type AlgorithmSigner interface { | |||
Signer | |||
// SignWithAlgorithm is like Signer.Sign, but allows specification of a | |||
// non-default signing algorithm. See the SigAlgo* constants in this | |||
// package for signature algorithms supported by this package. Callers may | |||
// pass an empty string for the algorithm in which case the AlgorithmSigner | |||
// will use its default algorithm. | |||
SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) | |||
} | |||
type rsaPublicKey rsa.PublicKey | |||
func (r *rsaPublicKey) Type() string { | |||
@@ -349,13 +372,21 @@ func (r *rsaPublicKey) Marshal() []byte { | |||
} | |||
func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { | |||
if sig.Format != r.Type() { | |||
var hash crypto.Hash | |||
switch sig.Format { | |||
case SigAlgoRSA: | |||
hash = crypto.SHA1 | |||
case SigAlgoRSASHA2256: | |||
hash = crypto.SHA256 | |||
case SigAlgoRSASHA2512: | |||
hash = crypto.SHA512 | |||
default: | |||
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) | |||
} | |||
h := crypto.SHA1.New() | |||
h := hash.New() | |||
h.Write(data) | |||
digest := h.Sum(nil) | |||
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) | |||
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, sig.Blob) | |||
} | |||
func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { | |||
@@ -459,6 +490,14 @@ func (k *dsaPrivateKey) PublicKey() PublicKey { | |||
} | |||
func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { | |||
return k.SignWithAlgorithm(rand, data, "") | |||
} | |||
func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { | |||
if algorithm != "" && algorithm != k.PublicKey().Type() { | |||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) | |||
} | |||
h := crypto.SHA1.New() | |||
h.Write(data) | |||
digest := h.Sum(nil) | |||
@@ -691,16 +730,42 @@ func (s *wrappedSigner) PublicKey() PublicKey { | |||
} | |||
func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { | |||
return s.SignWithAlgorithm(rand, data, "") | |||
} | |||
func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { | |||
var hashFunc crypto.Hash | |||
switch key := s.pubKey.(type) { | |||
case *rsaPublicKey, *dsaPublicKey: | |||
hashFunc = crypto.SHA1 | |||
case *ecdsaPublicKey: | |||
hashFunc = ecHash(key.Curve) | |||
case ed25519PublicKey: | |||
default: | |||
return nil, fmt.Errorf("ssh: unsupported key type %T", key) | |||
if _, ok := s.pubKey.(*rsaPublicKey); ok { | |||
// RSA keys support a few hash functions determined by the requested signature algorithm | |||
switch algorithm { | |||
case "", SigAlgoRSA: | |||
algorithm = SigAlgoRSA | |||
hashFunc = crypto.SHA1 | |||
case SigAlgoRSASHA2256: | |||
hashFunc = crypto.SHA256 | |||
case SigAlgoRSASHA2512: | |||
hashFunc = crypto.SHA512 | |||
default: | |||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) | |||
} | |||
} else { | |||
// The only supported algorithm for all other key types is the same as the type of the key | |||
if algorithm == "" { | |||
algorithm = s.pubKey.Type() | |||
} else if algorithm != s.pubKey.Type() { | |||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) | |||
} | |||
switch key := s.pubKey.(type) { | |||
case *dsaPublicKey: | |||
hashFunc = crypto.SHA1 | |||
case *ecdsaPublicKey: | |||
hashFunc = ecHash(key.Curve) | |||
case ed25519PublicKey: | |||
default: | |||
return nil, fmt.Errorf("ssh: unsupported key type %T", key) | |||
} | |||
} | |||
var digest []byte | |||
@@ -745,7 +810,7 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { | |||
} | |||
return &Signature{ | |||
Format: s.pubKey.Type(), | |||
Format: algorithm, | |||
Blob: signature, | |||
}, nil | |||
} |
@@ -350,8 +350,8 @@ func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.Public | |||
return db.checkAddr(hostToCheck, remoteKey) | |||
} | |||
// checkAddrs checks if we can find the given public key for any of | |||
// the given addresses. If we only find an entry for the IP address, | |||
// checkAddr checks if we can find the given public key for the | |||
// given address. If we only find an entry for the IP address, | |||
// or only the hostname, then this still succeeds. | |||
func (db *hostKeyDB) checkAddr(a addr, remoteKey ssh.PublicKey) error { | |||
// TODO(hanwen): are these the right semantics? What if there |
@@ -764,3 +764,29 @@ func decode(packet []byte) (interface{}, error) { | |||
} | |||
return msg, nil | |||
} | |||
var packetTypeNames = map[byte]string{ | |||
msgDisconnect: "disconnectMsg", | |||
msgServiceRequest: "serviceRequestMsg", | |||
msgServiceAccept: "serviceAcceptMsg", | |||
msgKexInit: "kexInitMsg", | |||
msgKexDHInit: "kexDHInitMsg", | |||
msgKexDHReply: "kexDHReplyMsg", | |||
msgUserAuthRequest: "userAuthRequestMsg", | |||
msgUserAuthSuccess: "userAuthSuccessMsg", | |||
msgUserAuthFailure: "userAuthFailureMsg", | |||
msgUserAuthPubKeyOk: "userAuthPubKeyOkMsg", | |||
msgGlobalRequest: "globalRequestMsg", | |||
msgRequestSuccess: "globalRequestSuccessMsg", | |||
msgRequestFailure: "globalRequestFailureMsg", | |||
msgChannelOpen: "channelOpenMsg", | |||
msgChannelData: "channelDataMsg", | |||
msgChannelOpenConfirm: "channelOpenConfirmMsg", | |||
msgChannelOpenFailure: "channelOpenFailureMsg", | |||
msgChannelWindowAdjust: "windowAdjustMsg", | |||
msgChannelEOF: "channelEOFMsg", | |||
msgChannelClose: "channelCloseMsg", | |||
msgChannelRequest: "channelRequestMsg", | |||
msgChannelSuccess: "channelRequestSuccessMsg", | |||
msgChannelFailure: "channelRequestFailureMsg", | |||
} |
@@ -404,7 +404,7 @@ userAuthLoop: | |||
perms, authErr = config.PasswordCallback(s, password) | |||
case "keyboard-interactive": | |||
if config.KeyboardInteractiveCallback == nil { | |||
authErr = errors.New("ssh: keyboard-interactive auth not configubred") | |||
authErr = errors.New("ssh: keyboard-interactive auth not configured") | |||
break | |||
} | |||
@@ -484,6 +484,7 @@ userAuthLoop: | |||
// sig.Format. This is usually the same, but | |||
// for certs, the names differ. | |||
if !isAcceptableAlgo(sig.Format) { | |||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format) | |||
break | |||
} | |||
signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData) |
@@ -53,14 +53,14 @@ type transport struct { | |||
// packetCipher represents a combination of SSH encryption/MAC | |||
// protocol. A single instance should be used for one direction only. | |||
type packetCipher interface { | |||
// writePacket encrypts the packet and writes it to w. The | |||
// writeCipherPacket encrypts the packet and writes it to w. The | |||
// contents of the packet are generally scrambled. | |||
writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error | |||
writeCipherPacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error | |||
// readPacket reads and decrypts a packet of data. The | |||
// readCipherPacket reads and decrypts a packet of data. The | |||
// returned packet may be overwritten by future calls of | |||
// readPacket. | |||
readPacket(seqnum uint32, r io.Reader) ([]byte, error) | |||
readCipherPacket(seqnum uint32, r io.Reader) ([]byte, error) | |||
} | |||
// connectionState represents one side (read or write) of the | |||
@@ -127,7 +127,7 @@ func (t *transport) readPacket() (p []byte, err error) { | |||
} | |||
func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { | |||
packet, err := s.packetCipher.readPacket(s.seqNum, r) | |||
packet, err := s.packetCipher.readCipherPacket(s.seqNum, r) | |||
s.seqNum++ | |||
if err == nil && len(packet) == 0 { | |||
err = errors.New("ssh: zero length packet") | |||
@@ -175,7 +175,7 @@ func (t *transport) writePacket(packet []byte) error { | |||
func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { | |||
changeKeys := len(packet) > 0 && packet[0] == msgNewKeys | |||
err := s.packetCipher.writePacket(s.seqNum, w, rand, packet) | |||
err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet) | |||
if err != nil { | |||
return err | |||
} |
@@ -0,0 +1,30 @@ | |||
// Copyright 2019 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 cpu | |||
import ( | |||
"encoding/binary" | |||
"runtime" | |||
) | |||
// hostByteOrder returns binary.LittleEndian on little-endian machines and | |||
// binary.BigEndian on big-endian machines. | |||
func hostByteOrder() binary.ByteOrder { | |||
switch runtime.GOARCH { | |||
case "386", "amd64", "amd64p32", | |||
"arm", "arm64", | |||
"mipsle", "mips64le", "mips64p32le", | |||
"ppc64le", | |||
"riscv", "riscv64": | |||
return binary.LittleEndian | |||
case "armbe", "arm64be", | |||
"mips", "mips64", "mips64p32", | |||
"ppc", "ppc64", | |||
"s390", "s390x", | |||
"sparc", "sparc64": | |||
return binary.BigEndian | |||
} | |||
panic("unknown architecture") | |||
} |
@@ -0,0 +1,126 @@ | |||
// Copyright 2018 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 cpu implements processor feature detection for | |||
// various CPU architectures. | |||
package cpu | |||
// Initialized reports whether the CPU features were initialized. | |||
// | |||
// For some GOOS/GOARCH combinations initialization of the CPU features depends | |||
// on reading an operating specific file, e.g. /proc/self/auxv on linux/arm | |||
// Initialized will report false if reading the file fails. | |||
var Initialized bool | |||
// CacheLinePad is used to pad structs to avoid false sharing. | |||
type CacheLinePad struct{ _ [cacheLineSize]byte } | |||
// X86 contains the supported CPU features of the | |||
// current X86/AMD64 platform. If the current platform | |||
// is not X86/AMD64 then all feature flags are false. | |||
// | |||
// X86 is padded to avoid false sharing. Further the HasAVX | |||
// and HasAVX2 are only set if the OS supports XMM and YMM | |||
// registers in addition to the CPUID feature bit being set. | |||
var X86 struct { | |||
_ CacheLinePad | |||
HasAES bool // AES hardware implementation (AES NI) | |||
HasADX bool // Multi-precision add-carry instruction extensions | |||
HasAVX bool // Advanced vector extension | |||
HasAVX2 bool // Advanced vector extension 2 | |||
HasBMI1 bool // Bit manipulation instruction set 1 | |||
HasBMI2 bool // Bit manipulation instruction set 2 | |||
HasERMS bool // Enhanced REP for MOVSB and STOSB | |||
HasFMA bool // Fused-multiply-add instructions | |||
HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. | |||
HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM | |||
HasPOPCNT bool // Hamming weight instruction POPCNT. | |||
HasRDRAND bool // RDRAND instruction (on-chip random number generator) | |||
HasRDSEED bool // RDSEED instruction (on-chip random number generator) | |||
HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64) | |||
HasSSE3 bool // Streaming SIMD extension 3 | |||
HasSSSE3 bool // Supplemental streaming SIMD extension 3 | |||
HasSSE41 bool // Streaming SIMD extension 4 and 4.1 | |||
HasSSE42 bool // Streaming SIMD extension 4 and 4.2 | |||
_ CacheLinePad | |||
} | |||
// ARM64 contains the supported CPU features of the | |||
// current ARMv8(aarch64) platform. If the current platform | |||
// is not arm64 then all feature flags are false. | |||
var ARM64 struct { | |||
_ CacheLinePad | |||
HasFP bool // Floating-point instruction set (always available) | |||
HasASIMD bool // Advanced SIMD (always available) | |||
HasEVTSTRM bool // Event stream support | |||
HasAES bool // AES hardware implementation | |||
HasPMULL bool // Polynomial multiplication instruction set | |||
HasSHA1 bool // SHA1 hardware implementation | |||
HasSHA2 bool // SHA2 hardware implementation | |||
HasCRC32 bool // CRC32 hardware implementation | |||
HasATOMICS bool // Atomic memory operation instruction set | |||
HasFPHP bool // Half precision floating-point instruction set | |||
HasASIMDHP bool // Advanced SIMD half precision instruction set | |||
HasCPUID bool // CPUID identification scheme registers | |||
HasASIMDRDM bool // Rounding double multiply add/subtract instruction set | |||
HasJSCVT bool // Javascript conversion from floating-point to integer | |||
HasFCMA bool // Floating-point multiplication and addition of complex numbers | |||
HasLRCPC bool // Release Consistent processor consistent support | |||
HasDCPOP bool // Persistent memory support | |||
HasSHA3 bool // SHA3 hardware implementation | |||
HasSM3 bool // SM3 hardware implementation | |||
HasSM4 bool // SM4 hardware implementation | |||
HasASIMDDP bool // Advanced SIMD double precision instruction set | |||
HasSHA512 bool // SHA512 hardware implementation | |||
HasSVE bool // Scalable Vector Extensions | |||
HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32 | |||
_ CacheLinePad | |||
} | |||
// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms. | |||
// If the current platform is not ppc64/ppc64le then all feature flags are false. | |||
// | |||
// For ppc64/ppc64le, it is safe to check only for ISA level starting on ISA v3.00, | |||
// since there are no optional categories. There are some exceptions that also | |||
// require kernel support to work (DARN, SCV), so there are feature bits for | |||
// those as well. The minimum processor requirement is POWER8 (ISA 2.07). | |||
// The struct is padded to avoid false sharing. | |||
var PPC64 struct { | |||
_ CacheLinePad | |||
HasDARN bool // Hardware random number generator (requires kernel enablement) | |||
HasSCV bool // Syscall vectored (requires kernel enablement) | |||
IsPOWER8 bool // ISA v2.07 (POWER8) | |||
IsPOWER9 bool // ISA v3.00 (POWER9) | |||
_ CacheLinePad | |||
} | |||
// S390X contains the supported CPU features of the current IBM Z | |||
// (s390x) platform. If the current platform is not IBM Z then all | |||
// feature flags are false. | |||
// | |||
// S390X is padded to avoid false sharing. Further HasVX is only set | |||
// if the OS supports vector registers in addition to the STFLE | |||
// feature bit being set. | |||
var S390X struct { | |||
_ CacheLinePad | |||
HasZARCH bool // z/Architecture mode is active [mandatory] | |||
HasSTFLE bool // store facility list extended | |||
HasLDISP bool // long (20-bit) displacements | |||
HasEIMM bool // 32-bit immediates | |||
HasDFP bool // decimal floating point | |||
HasETF3EH bool // ETF-3 enhanced | |||
HasMSA bool // message security assist (CPACF) | |||
HasAES bool // KM-AES{128,192,256} functions | |||
HasAESCBC bool // KMC-AES{128,192,256} functions | |||
HasAESCTR bool // KMCTR-AES{128,192,256} functions | |||
HasAESGCM bool // KMA-GCM-AES{128,192,256} functions | |||
HasGHASH bool // KIMD-GHASH function | |||
HasSHA1 bool // K{I,L}MD-SHA-1 functions | |||
HasSHA256 bool // K{I,L}MD-SHA-256 functions | |||
HasSHA512 bool // K{I,L}MD-SHA-512 functions | |||
HasSHA3 bool // K{I,L}MD-SHA3-{224,256,384,512} and K{I,L}MD-SHAKE-{128,256} functions | |||
HasVX bool // vector facility | |||
HasVXE bool // vector-enhancements facility 1 | |||
_ CacheLinePad | |||
} |
@@ -0,0 +1,30 @@ | |||
// Copyright 2019 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 aix,ppc64 | |||
package cpu | |||
import "golang.org/x/sys/unix" | |||
const cacheLineSize = 128 | |||
const ( | |||
// getsystemcfg constants | |||
_SC_IMPL = 2 | |||
_IMPL_POWER8 = 0x10000 | |||
_IMPL_POWER9 = 0x20000 | |||
) | |||
func init() { | |||
impl := unix.Getsystemcfg(_SC_IMPL) | |||
if impl&_IMPL_POWER8 != 0 { | |||
PPC64.IsPOWER8 = true | |||
} | |||
if impl&_IMPL_POWER9 != 0 { | |||
PPC64.IsPOWER9 = true | |||
} | |||
Initialized = true | |||
} |
@@ -0,0 +1,9 @@ | |||
// Copyright 2018 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 cpu | |||
const cacheLineSize = 32 | |||
func doinit() {} |
@@ -0,0 +1,21 @@ | |||
// Copyright 2019 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 !gccgo | |||
package cpu | |||
// haveAsmFunctions reports whether the other functions in this file can | |||
// be safely called. | |||
func haveAsmFunctions() bool { return true } | |||
// The following feature detection functions are defined in cpu_s390x.s. | |||
// They are likely to be expensive to call so the results should be cached. | |||
func stfle() facilityList | |||
func kmQuery() queryResult | |||
func kmcQuery() queryResult | |||
func kmctrQuery() queryResult | |||
func kmaQuery() queryResult | |||
func kimdQuery() queryResult | |||
func klmdQuery() queryResult |
@@ -0,0 +1,16 @@ | |||
// Copyright 2018 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 386 amd64 amd64p32 | |||
// +build !gccgo | |||
package cpu | |||
// cpuid is implemented in cpu_x86.s for gc compiler | |||
// and in cpu_gccgo.c for gccgo. | |||
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) | |||
// xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler | |||
// and in cpu_gccgo.c for gccgo. | |||
func xgetbv() (eax, edx uint32) |
@@ -0,0 +1,43 @@ | |||
// Copyright 2018 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 386 amd64 amd64p32 | |||
// +build gccgo | |||
#include <cpuid.h> | |||
#include <stdint.h> | |||
// Need to wrap __get_cpuid_count because it's declared as static. | |||
int | |||
gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf, | |||
uint32_t *eax, uint32_t *ebx, | |||
uint32_t *ecx, uint32_t *edx) | |||
{ | |||
return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx); | |||
} | |||
// xgetbv reads the contents of an XCR (Extended Control Register) | |||
// specified in the ECX register into registers EDX:EAX. | |||
// Currently, the only supported value for XCR is 0. | |||
// | |||
// TODO: Replace with a better alternative: | |||
// | |||
// #include <xsaveintrin.h> | |||
// | |||
// #pragma GCC target("xsave") | |||
// | |||
// void gccgoXgetbv(uint32_t *eax, uint32_t *edx) { | |||
// unsigned long long x = _xgetbv(0); | |||
// *eax = x & 0xffffffff; | |||
// *edx = (x >> 32) & 0xffffffff; | |||
// } | |||
// | |||
// Note that _xgetbv is defined starting with GCC 8. | |||
void | |||
gccgoXgetbv(uint32_t *eax, uint32_t *edx) | |||
{ | |||
__asm(" xorl %%ecx, %%ecx\n" | |||
" xgetbv" | |||
: "=a"(*eax), "=d"(*edx)); | |||
} |
@@ -0,0 +1,26 @@ | |||
// Copyright 2018 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 386 amd64 amd64p32 | |||
// +build gccgo | |||
package cpu | |||
//extern gccgoGetCpuidCount | |||
func gccgoGetCpuidCount(eaxArg, ecxArg uint32, eax, ebx, ecx, edx *uint32) | |||
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) { | |||
var a, b, c, d uint32 | |||
gccgoGetCpuidCount(eaxArg, ecxArg, &a, &b, &c, &d) | |||
return a, b, c, d | |||
} | |||
//extern gccgoXgetbv | |||
func gccgoXgetbv(eax, edx *uint32) | |||
func xgetbv() (eax, edx uint32) { | |||
var a, d uint32 | |||
gccgoXgetbv(&a, &d) | |||
return a, d | |||
} |
@@ -0,0 +1,22 @@ | |||
// Copyright 2019 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 gccgo | |||
package cpu | |||
// haveAsmFunctions reports whether the other functions in this file can | |||
// be safely called. | |||
func haveAsmFunctions() bool { return false } | |||
// TODO(mundaym): the following feature detection functions are currently | |||
// stubs. See https://golang.org/cl/162887 for how to fix this. | |||
// They are likely to be expensive to call so the results should be cached. | |||
func stfle() facilityList { panic("not implemented for gccgo") } | |||
func kmQuery() queryResult { panic("not implemented for gccgo") } | |||
func kmcQuery() queryResult { panic("not implemented for gccgo") } | |||
func kmctrQuery() queryResult { panic("not implemented for gccgo") } | |||
func kmaQuery() queryResult { panic("not implemented for gccgo") } | |||
func kimdQuery() queryResult { panic("not implemented for gccgo") } | |||
func klmdQuery() queryResult { panic("not implemented for gccgo") } |
@@ -0,0 +1,59 @@ | |||
// Copyright 2018 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 !amd64,!amd64p32,!386 | |||
package cpu | |||
import ( | |||
"io/ioutil" | |||
) | |||
const ( | |||
_AT_HWCAP = 16 | |||
_AT_HWCAP2 = 26 | |||
procAuxv = "/proc/self/auxv" | |||
uintSize = int(32 << (^uint(0) >> 63)) | |||
) | |||
// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2 | |||
// These are initialized in cpu_$GOARCH.go | |||
// and should not be changed after they are initialized. | |||
var hwCap uint | |||
var hwCap2 uint | |||
func init() { | |||
buf, err := ioutil.ReadFile(procAuxv) | |||
if err != nil { | |||
// e.g. on android /proc/self/auxv is not accessible, so silently | |||
// ignore the error and leave Initialized = false | |||
return | |||
} | |||
bo := hostByteOrder() | |||
for len(buf) >= 2*(uintSize/8) { | |||
var tag, val uint | |||
switch uintSize { | |||
case 32: | |||
tag = uint(bo.Uint32(buf[0:])) | |||
val = uint(bo.Uint32(buf[4:])) | |||
buf = buf[8:] | |||
case 64: | |||
tag = uint(bo.Uint64(buf[0:])) | |||
val = uint(bo.Uint64(buf[8:])) | |||
buf = buf[16:] | |||
} | |||
switch tag { | |||
case _AT_HWCAP: | |||
hwCap = val | |||
case _AT_HWCAP2: | |||
hwCap2 = val | |||
} | |||
} | |||
doinit() | |||
Initialized = true | |||
} |
@@ -0,0 +1,67 @@ | |||
// Copyright 2018 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 cpu | |||
const cacheLineSize = 64 | |||
// HWCAP/HWCAP2 bits. These are exposed by Linux. | |||
const ( | |||
hwcap_FP = 1 << 0 | |||
hwcap_ASIMD = 1 << 1 | |||
hwcap_EVTSTRM = 1 << 2 | |||
hwcap_AES = 1 << 3 | |||
hwcap_PMULL = 1 << 4 | |||
hwcap_SHA1 = 1 << 5 | |||
hwcap_SHA2 = 1 << 6 | |||
hwcap_CRC32 = 1 << 7 | |||
hwcap_ATOMICS = 1 << 8 | |||
hwcap_FPHP = 1 << 9 | |||
hwcap_ASIMDHP = 1 << 10 | |||
hwcap_CPUID = 1 << 11 | |||
hwcap_ASIMDRDM = 1 << 12 | |||
hwcap_JSCVT = 1 << 13 | |||
hwcap_FCMA = 1 << 14 | |||
hwcap_LRCPC = 1 << 15 | |||
hwcap_DCPOP = 1 << 16 | |||
hwcap_SHA3 = 1 << 17 | |||
hwcap_SM3 = 1 << 18 | |||
hwcap_SM4 = 1 << 19 | |||
hwcap_ASIMDDP = 1 << 20 | |||
hwcap_SHA512 = 1 << 21 | |||
hwcap_SVE = 1 << 22 | |||
hwcap_ASIMDFHM = 1 << 23 | |||
) | |||
func doinit() { | |||
// HWCAP feature bits | |||
ARM64.HasFP = isSet(hwCap, hwcap_FP) | |||
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD) | |||
ARM64.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM) | |||
ARM64.HasAES = isSet(hwCap, hwcap_AES) | |||
ARM64.HasPMULL = isSet(hwCap, hwcap_PMULL) | |||
ARM64.HasSHA1 = isSet(hwCap, hwcap_SHA1) | |||
ARM64.HasSHA2 = isSet(hwCap, hwcap_SHA2) | |||
ARM64.HasCRC32 = isSet(hwCap, hwcap_CRC32) | |||
ARM64.HasATOMICS = isSet(hwCap, hwcap_ATOMICS) | |||
ARM64.HasFPHP = isSet(hwCap, hwcap_FPHP) | |||
ARM64.HasASIMDHP = isSet(hwCap, hwcap_ASIMDHP) | |||
ARM64.HasCPUID = isSet(hwCap, hwcap_CPUID) | |||
ARM64.HasASIMDRDM = isSet(hwCap, hwcap_ASIMDRDM) | |||
ARM64.HasJSCVT = isSet(hwCap, hwcap_JSCVT) | |||
ARM64.HasFCMA = isSet(hwCap, hwcap_FCMA) | |||
ARM64.HasLRCPC = isSet(hwCap, hwcap_LRCPC) | |||
ARM64.HasDCPOP = isSet(hwCap, hwcap_DCPOP) | |||
ARM64.HasSHA3 = isSet(hwCap, hwcap_SHA3) | |||
ARM64.HasSM3 = isSet(hwCap, hwcap_SM3) | |||
ARM64.HasSM4 = isSet(hwCap, hwcap_SM4) | |||
ARM64.HasASIMDDP = isSet(hwCap, hwcap_ASIMDDP) | |||
ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512) | |||
ARM64.HasSVE = isSet(hwCap, hwcap_SVE) | |||
ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM) | |||
} | |||
func isSet(hwc uint, value uint) bool { | |||
return hwc&value != 0 | |||
} |
@@ -0,0 +1,33 @@ | |||
// Copyright 2018 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 | |||
// +build ppc64 ppc64le | |||
package cpu | |||
const cacheLineSize = 128 | |||
// HWCAP/HWCAP2 bits. These are exposed by the kernel. | |||
const ( | |||
// ISA Level | |||
_PPC_FEATURE2_ARCH_2_07 = 0x80000000 | |||
_PPC_FEATURE2_ARCH_3_00 = 0x00800000 | |||
// CPU features | |||
_PPC_FEATURE2_DARN = 0x00200000 | |||
_PPC_FEATURE2_SCV = 0x00100000 | |||
) | |||
func doinit() { | |||
// HWCAP2 feature bits | |||
PPC64.IsPOWER8 = isSet(hwCap2, _PPC_FEATURE2_ARCH_2_07) | |||
PPC64.IsPOWER9 = isSet(hwCap2, _PPC_FEATURE2_ARCH_3_00) | |||
PPC64.HasDARN = isSet(hwCap2, _PPC_FEATURE2_DARN) | |||
PPC64.HasSCV = isSet(hwCap2, _PPC_FEATURE2_SCV) | |||
} | |||
func isSet(hwc uint, value uint) bool { | |||
return hwc&value != 0 | |||
} |
@@ -0,0 +1,161 @@ | |||
// Copyright 2019 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 cpu | |||
const cacheLineSize = 256 | |||
const ( | |||
// bit mask values from /usr/include/bits/hwcap.h | |||
hwcap_ZARCH = 2 | |||
hwcap_STFLE = 4 | |||
hwcap_MSA = 8 | |||
hwcap_LDISP = 16 | |||
hwcap_EIMM = 32 | |||
hwcap_DFP = 64 | |||
hwcap_ETF3EH = 256 | |||
hwcap_VX = 2048 | |||
hwcap_VXE = 8192 | |||
) | |||
// bitIsSet reports whether the bit at index is set. The bit index | |||
// is in big endian order, so bit index 0 is the leftmost bit. | |||
func bitIsSet(bits []uint64, index uint) bool { | |||
return bits[index/64]&((1<<63)>>(index%64)) != 0 | |||
} | |||
// function is the code for the named cryptographic function. | |||
type function uint8 | |||
const ( | |||
// KM{,A,C,CTR} function codes | |||
aes128 function = 18 // AES-128 | |||
aes192 function = 19 // AES-192 | |||
aes256 function = 20 // AES-256 | |||
// K{I,L}MD function codes | |||
sha1 function = 1 // SHA-1 | |||
sha256 function = 2 // SHA-256 | |||
sha512 function = 3 // SHA-512 | |||
sha3_224 function = 32 // SHA3-224 | |||
sha3_256 function = 33 // SHA3-256 | |||
sha3_384 function = 34 // SHA3-384 | |||
sha3_512 function = 35 // SHA3-512 | |||
shake128 function = 36 // SHAKE-128 | |||
shake256 function = 37 // SHAKE-256 | |||
// KLMD function codes | |||
ghash function = 65 // GHASH | |||
) | |||
// queryResult contains the result of a Query function | |||
// call. Bits are numbered in big endian order so the | |||
// leftmost bit (the MSB) is at index 0. | |||
type queryResult struct { | |||
bits [2]uint64 | |||
} | |||
// Has reports whether the given functions are present. | |||
func (q *queryResult) Has(fns ...function) bool { | |||
if len(fns) == 0 { | |||
panic("no function codes provided") | |||
} | |||
for _, f := range fns { | |||
if !bitIsSet(q.bits[:], uint(f)) { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
// facility is a bit index for the named facility. | |||
type facility uint8 | |||
const ( | |||
// cryptography facilities | |||
msa4 facility = 77 // message-security-assist extension 4 | |||
msa8 facility = 146 // message-security-assist extension 8 | |||
) | |||
// facilityList contains the result of an STFLE call. | |||
// Bits are numbered in big endian order so the | |||
// leftmost bit (the MSB) is at index 0. | |||
type facilityList struct { | |||
bits [4]uint64 | |||
} | |||
// Has reports whether the given facilities are present. | |||
func (s *facilityList) Has(fs ...facility) bool { | |||
if len(fs) == 0 { | |||
panic("no facility bits provided") | |||
} | |||
for _, f := range fs { | |||
if !bitIsSet(s.bits[:], uint(f)) { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
func doinit() { | |||
// test HWCAP bit vector | |||
has := func(featureMask uint) bool { | |||
return hwCap&featureMask == featureMask | |||
} | |||
// mandatory | |||
S390X.HasZARCH = has(hwcap_ZARCH) | |||
// optional | |||
S390X.HasSTFLE = has(hwcap_STFLE) | |||
S390X.HasLDISP = has(hwcap_LDISP) | |||
S390X.HasEIMM = has(hwcap_EIMM) | |||
S390X.HasETF3EH = has(hwcap_ETF3EH) | |||
S390X.HasDFP = has(hwcap_DFP) | |||
S390X.HasMSA = has(hwcap_MSA) | |||
S390X.HasVX = has(hwcap_VX) | |||
if S390X.HasVX { | |||
S390X.HasVXE = has(hwcap_VXE) | |||
} | |||
// We need implementations of stfle, km and so on | |||
// to detect cryptographic features. | |||
if !haveAsmFunctions() { | |||
return | |||
} | |||
// optional cryptographic functions | |||
if S390X.HasMSA { | |||
aes := []function{aes128, aes192, aes256} | |||
// cipher message | |||
km, kmc := kmQuery(), kmcQuery() | |||
S390X.HasAES = km.Has(aes...) | |||
S390X.HasAESCBC = kmc.Has(aes...) | |||
if S390X.HasSTFLE { | |||
facilities := stfle() | |||
if facilities.Has(msa4) { | |||
kmctr := kmctrQuery() | |||
S390X.HasAESCTR = kmctr.Has(aes...) | |||
} | |||
if facilities.Has(msa8) { | |||
kma := kmaQuery() | |||
S390X.HasAESGCM = kma.Has(aes...) | |||
} | |||
} | |||
// compute message digest | |||
kimd := kimdQuery() // intermediate (no padding) | |||
klmd := klmdQuery() // last (padding) | |||
S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1) | |||
S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256) | |||
S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512) | |||
S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist | |||
sha3 := []function{ | |||
sha3_224, sha3_256, sha3_384, sha3_512, | |||
shake128, shake256, | |||
} | |||
S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...) | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
// Copyright 2018 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 mips64 mips64le | |||
package cpu | |||
const cacheLineSize = 32 | |||
func doinit() {} |
@@ -0,0 +1,11 @@ | |||
// Copyright 2018 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 mips mipsle | |||
package cpu | |||
const cacheLineSize = 32 | |||
func doinit() {} |
@@ -0,0 +1,11 @@ | |||
// Copyright 2019 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,arm64 | |||
package cpu | |||
const cacheLineSize = 64 | |||
func doinit() {} |
@@ -0,0 +1,57 @@ | |||
// Copyright 2019 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 !gccgo | |||
#include "textflag.h" | |||
// func stfle() facilityList | |||
TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32 | |||
MOVD $ret+0(FP), R1 | |||
MOVD $3, R0 // last doubleword index to store | |||
XC $32, (R1), (R1) // clear 4 doublewords (32 bytes) | |||
WORD $0xb2b01000 // store facility list extended (STFLE) | |||
RET | |||
// func kmQuery() queryResult | |||
TEXT ·kmQuery(SB), NOSPLIT|NOFRAME, $0-16 | |||
MOVD $0, R0 // set function code to 0 (KM-Query) | |||
MOVD $ret+0(FP), R1 // address of 16-byte return value | |||
WORD $0xB92E0024 // cipher message (KM) | |||
RET | |||
// func kmcQuery() queryResult | |||
TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16 | |||
MOVD $0, R0 // set function code to 0 (KMC-Query) | |||
MOVD $ret+0(FP), R1 // address of 16-byte return value | |||
WORD $0xB92F0024 // cipher message with chaining (KMC) | |||
RET | |||
// func kmctrQuery() queryResult | |||
TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16 | |||
MOVD $0, R0 // set function code to 0 (KMCTR-Query) | |||
MOVD $ret+0(FP), R1 // address of 16-byte return value | |||
WORD $0xB92D4024 // cipher message with counter (KMCTR) | |||
RET | |||
// func kmaQuery() queryResult | |||
TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16 | |||
MOVD $0, R0 // set function code to 0 (KMA-Query) | |||
MOVD $ret+0(FP), R1 // address of 16-byte return value | |||
WORD $0xb9296024 // cipher message with authentication (KMA) | |||
RET | |||
// func kimdQuery() queryResult | |||
TEXT ·kimdQuery(SB), NOSPLIT|NOFRAME, $0-16 | |||
MOVD $0, R0 // set function code to 0 (KIMD-Query) | |||
MOVD $ret+0(FP), R1 // address of 16-byte return value | |||
WORD $0xB93E0024 // compute intermediate message digest (KIMD) | |||
RET | |||
// func klmdQuery() queryResult | |||
TEXT ·klmdQuery(SB), NOSPLIT|NOFRAME, $0-16 | |||
MOVD $0, R0 // set function code to 0 (KLMD-Query) | |||
MOVD $ret+0(FP), R1 // address of 16-byte return value | |||
WORD $0xB93F0024 // compute last message digest (KLMD) | |||
RET |
@@ -0,0 +1,15 @@ | |||
// Copyright 2019 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 wasm | |||
package cpu | |||
// We're compiling the cpu package for an unknown (software-abstracted) CPU. | |||
// Make CacheLinePad an empty struct and hope that the usual struct alignment | |||
// rules are good enough. | |||
const cacheLineSize = 0 | |||
func doinit() {} |
@@ -0,0 +1,59 @@ | |||
// Copyright 2018 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 386 amd64 amd64p32 | |||
package cpu | |||
const cacheLineSize = 64 | |||
func init() { | |||
Initialized = true | |||
maxID, _, _, _ := cpuid(0, 0) | |||
if maxID < 1 { | |||
return | |||
} | |||
_, _, ecx1, edx1 := cpuid(1, 0) | |||
X86.HasSSE2 = isSet(26, edx1) | |||
X86.HasSSE3 = isSet(0, ecx1) | |||
X86.HasPCLMULQDQ = isSet(1, ecx1) | |||
X86.HasSSSE3 = isSet(9, ecx1) | |||
X86.HasFMA = isSet(12, ecx1) | |||
X86.HasSSE41 = isSet(19, ecx1) | |||
X86.HasSSE42 = isSet(20, ecx1) | |||
X86.HasPOPCNT = isSet(23, ecx1) | |||
X86.HasAES = isSet(25, ecx1) | |||
X86.HasOSXSAVE = isSet(27, ecx1) | |||
X86.HasRDRAND = isSet(30, ecx1) | |||
osSupportsAVX := false | |||
// For XGETBV, OSXSAVE bit is required and sufficient. | |||
if X86.HasOSXSAVE { | |||
eax, _ := xgetbv() | |||
// Check if XMM and YMM registers have OS support. | |||
osSupportsAVX = isSet(1, eax) && isSet(2, eax) | |||
} | |||
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX | |||
if maxID < 7 { | |||
return | |||
} | |||
_, ebx7, _, _ := cpuid(7, 0) | |||
X86.HasBMI1 = isSet(3, ebx7) | |||
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX | |||
X86.HasBMI2 = isSet(8, ebx7) | |||
X86.HasERMS = isSet(9, ebx7) | |||
X86.HasRDSEED = isSet(18, ebx7) | |||
X86.HasADX = isSet(19, ebx7) | |||
} | |||
func isSet(bitpos uint, value uint32) bool { | |||
return value&(1<<bitpos) != 0 | |||
} |
@@ -0,0 +1,27 @@ | |||
// Copyright 2018 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 386 amd64 amd64p32 | |||
// +build !gccgo | |||
#include "textflag.h" | |||
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) | |||
TEXT ·cpuid(SB), NOSPLIT, $0-24 | |||
MOVL eaxArg+0(FP), AX | |||
MOVL ecxArg+4(FP), CX | |||
CPUID | |||
MOVL AX, eax+8(FP) | |||
MOVL BX, ebx+12(FP) | |||
MOVL CX, ecx+16(FP) | |||
MOVL DX, edx+20(FP) | |||
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 |
@@ -207,8 +207,6 @@ esac | |||
esac | |||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi | |||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi | |||
if [ -n "$mktypes" ]; then | |||
echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go"; | |||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go"; fi | |||
if [ -n "$mkasm" ]; then echo "$mkasm $GOARCH"; fi | |||
fi | |||
) | $run |
@@ -192,6 +192,7 @@ struct ltchars { | |||
#include <linux/if_packet.h> | |||
#include <linux/if_addr.h> | |||
#include <linux/falloc.h> | |||
#include <linux/fanotify.h> | |||
#include <linux/filter.h> | |||
#include <linux/fs.h> | |||
#include <linux/kexec.h> | |||
@@ -501,6 +502,7 @@ ccflags="$@" | |||
$2 !~ "WMESGLEN" && | |||
$2 ~ /^W[A-Z0-9]+$/ || | |||
$2 ~/^PPPIOC/ || | |||
$2 ~ /^FAN_|FANOTIFY_/ || | |||
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)} | |||
$2 ~ /^__WCOREFLAG$/ {next} | |||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} |
@@ -153,6 +153,11 @@ func main() { | |||
} | |||
funct, inps, outps, sysname := f[2], f[3], f[4], f[5] | |||
// ClockGettime doesn't have a syscall number on Darwin, only generate libc wrappers. | |||
if goos == "darwin" && !libc && funct == "ClockGettime" { | |||
continue | |||
} | |||
// Split argument lists on comma. | |||
in := parseParamList(inps) | |||
out := parseParamList(outps) | |||
@@ -228,7 +233,7 @@ func main() { | |||
} else { | |||
args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) | |||
} | |||
} else if p.Type == "int64" && endianness != "" { | |||
} else if (p.Type == "int64" || p.Type == "uint64") && endianness != "" { | |||
if len(args)%2 == 1 && *arm { | |||
// arm abi specifies 64-bit argument uses | |||
// (even, odd) pair |
@@ -25,8 +25,8 @@ func cmsgAlignOf(salen int) int { | |||
if SizeofPtr == 8 { | |||
salign = 4 | |||
} | |||
case "openbsd": | |||
// OpenBSD armv7 requires 64-bit alignment. | |||
case "netbsd", "openbsd": | |||
// NetBSD and OpenBSD armv7 require 64-bit alignment. | |||
if runtime.GOARCH == "arm" { | |||
salign = 8 | |||
} |
@@ -545,3 +545,5 @@ func Poll(fds []PollFd, timeout int) (n int, err error) { | |||
//sys gettimeofday(tv *Timeval, tzp *Timezone) (err error) | |||
//sysnb Time(t *Time_t) (tt Time_t, err error) | |||
//sys Utime(path string, buf *Utimbuf) (err error) | |||
//sys Getsystemcfg(label int) (n uint64) |
@@ -144,6 +144,23 @@ func getAttrList(path string, attrList attrList, attrBuf []byte, options uint) ( | |||
//sys getattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) | |||
func SysctlClockinfo(name string) (*Clockinfo, error) { | |||
mib, err := sysctlmib(name) | |||
if err != nil { | |||
return nil, err | |||
} | |||
n := uintptr(SizeofClockinfo) | |||
var ci Clockinfo | |||
if err := sysctl(mib, (*byte)(unsafe.Pointer(&ci)), &n, nil, 0); err != nil { | |||
return nil, err | |||
} | |||
if n != SizeofClockinfo { | |||
return nil, EIO | |||
} | |||
return &ci, nil | |||
} | |||
//sysnb pipe() (r int, w int, err error) | |||
func Pipe(p []int) (err error) { |
@@ -39,6 +39,20 @@ func Creat(path string, mode uint32) (fd int, err error) { | |||
return Open(path, O_CREAT|O_WRONLY|O_TRUNC, mode) | |||
} | |||
//sys FanotifyInit(flags uint, event_f_flags uint) (fd int, err error) | |||
//sys fanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname *byte) (err error) | |||
func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) (err error) { | |||
if pathname == "" { | |||
return fanotifyMark(fd, flags, mask, dirFd, nil) | |||
} | |||
p, err := BytePtrFromString(pathname) | |||
if err != nil { | |||
return err | |||
} | |||
return fanotifyMark(fd, flags, mask, dirFd, p) | |||
} | |||
//sys fchmodat(dirfd int, path string, mode uint32) (err error) | |||
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { | |||
@@ -990,10 +1004,28 @@ func GetsockoptString(fd, level, opt int) (string, error) { | |||
return string(buf[:vallen-1]), nil | |||
} | |||
func GetsockoptTpacketStats(fd, level, opt int) (*TpacketStats, error) { | |||
var value TpacketStats | |||
vallen := _Socklen(SizeofTpacketStats) | |||
err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen) | |||
return &value, err | |||
} | |||
func GetsockoptTpacketStatsV3(fd, level, opt int) (*TpacketStatsV3, error) { | |||
var value TpacketStatsV3 | |||
vallen := _Socklen(SizeofTpacketStatsV3) | |||
err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen) | |||
return &value, err | |||
} | |||
func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) { | |||
return setsockopt(fd, level, opt, unsafe.Pointer(mreq), unsafe.Sizeof(*mreq)) | |||
} | |||
func SetsockoptPacketMreq(fd, level, opt int, mreq *PacketMreq) error { | |||
return setsockopt(fd, level, opt, unsafe.Pointer(mreq), unsafe.Sizeof(*mreq)) | |||
} | |||
// SetsockoptSockFprog attaches a classic BPF or an extended BPF program to a | |||
// socket to filter incoming packets. See 'man 7 socket' for usage information. | |||
func SetsockoptSockFprog(fd, level, opt int, fprog *SockFprog) error { | |||
@@ -1008,6 +1040,14 @@ func SetsockoptCanRawFilter(fd, level, opt int, filter []CanFilter) error { | |||
return setsockopt(fd, level, opt, p, uintptr(len(filter)*SizeofCanFilter)) | |||
} | |||
func SetsockoptTpacketReq(fd, level, opt int, tp *TpacketReq) error { | |||
return setsockopt(fd, level, opt, unsafe.Pointer(tp), unsafe.Sizeof(*tp)) | |||
} | |||
func SetsockoptTpacketReq3(fd, level, opt int, tp *TpacketReq3) error { | |||
return setsockopt(fd, level, opt, unsafe.Pointer(tp), unsafe.Sizeof(*tp)) | |||
} | |||
// Keyctl Commands (http://man7.org/linux/man-pages/man2/keyctl.2.html) | |||
// KeyctlInt calls keyctl commands in which each argument is an int. |
@@ -19,12 +19,18 @@ func setTimeval(sec, usec int64) Timeval { | |||
return Timeval{Sec: int32(sec), Usec: int32(usec)} | |||
} | |||
//sysnb pipe(p *[2]_C_int) (err error) | |||
func Pipe(p []int) (err error) { | |||
if len(p) != 2 { | |||
return EINVAL | |||
} | |||
var pp [2]_C_int | |||
// Try pipe2 first for Android O, then try pipe for kernel 2.6.23. | |||
err = pipe2(&pp, 0) | |||
if err == ENOSYS { | |||
err = pipe(&pp) | |||
} | |||
p[0] = int(pp[0]) | |||
p[1] = int(pp[1]) | |||
return |
@@ -208,3 +208,16 @@ func Poll(fds []PollFd, timeout int) (n int, err error) { | |||
} | |||
return ppoll(&fds[0], len(fds), ts, nil) | |||
} | |||
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error) | |||
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error { | |||
cmdlineLen := len(cmdline) | |||
if cmdlineLen > 0 { | |||
// Account for the additional NULL byte added by | |||
// BytePtrFromString in kexecFileLoad. The kexec_file_load | |||
// syscall expects a NULL-terminated string. | |||
cmdlineLen++ | |||
} | |||
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags) | |||
} |
@@ -211,3 +211,16 @@ func Poll(fds []PollFd, timeout int) (n int, err error) { | |||
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { | |||
return Renameat2(olddirfd, oldpath, newdirfd, newpath, 0) | |||
} | |||
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error) | |||
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error { | |||
cmdlineLen := len(cmdline) | |||
if cmdlineLen > 0 { | |||
// Account for the additional NULL byte added by | |||
// BytePtrFromString in kexecFileLoad. The kexec_file_load | |||
// syscall expects a NULL-terminated string. | |||
cmdlineLen++ | |||
} | |||
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags) | |||
} |
@@ -43,6 +43,23 @@ func nametomib(name string) (mib []_C_int, err error) { | |||
return nil, EINVAL | |||
} | |||
func SysctlClockinfo(name string) (*Clockinfo, error) { | |||
mib, err := sysctlmib(name) | |||
if err != nil { | |||
return nil, err | |||
} | |||
n := uintptr(SizeofClockinfo) | |||
var ci Clockinfo | |||
if err := sysctl(mib, (*byte)(unsafe.Pointer(&ci)), &n, nil, 0); err != nil { | |||
return nil, err | |||
} | |||
if n != SizeofClockinfo { | |||
return nil, EIO | |||
} | |||
return &ci, nil | |||
} | |||
func SysctlUvmexp(name string) (*Uvmexp, error) { | |||
mib, err := sysctlmib(name) | |||
if err != nil { |
@@ -28,6 +28,11 @@ var ( | |||
errENOENT error = syscall.ENOENT | |||
) | |||
var ( | |||
signalNameMapOnce sync.Once | |||
signalNameMap map[string]syscall.Signal | |||
) | |||
// errnoErr returns common boxed Errno values, to prevent | |||
// allocations at runtime. | |||
func errnoErr(e syscall.Errno) error { | |||
@@ -66,6 +71,19 @@ func SignalName(s syscall.Signal) string { | |||
return "" | |||
} | |||
// SignalNum returns the syscall.Signal for signal named s, | |||
// or 0 if a signal with such name is not found. | |||
// The signal name should start with "SIG". | |||
func SignalNum(s string) syscall.Signal { | |||
signalNameMapOnce.Do(func() { | |||
signalNameMap = make(map[string]syscall.Signal) | |||
for _, signal := range signalList { | |||
signalNameMap[signal.name] = signal.num | |||
} | |||
}) | |||
return signalNameMap[s] | |||
} | |||
// clen returns the index of the first NULL byte in n or len(n) if n contains no NULL byte. | |||
func clen(n []byte) int { | |||
i := bytes.IndexByte(n, 0) | |||
@@ -276,6 +294,13 @@ func GetsockoptTimeval(fd, level, opt int) (*Timeval, error) { | |||
return &tv, err | |||
} | |||
func GetsockoptUint64(fd, level, opt int) (value uint64, err error) { | |||
var n uint64 | |||
vallen := _Socklen(8) | |||
err = getsockopt(fd, level, opt, unsafe.Pointer(&n), &vallen) | |||
return n, err | |||
} | |||
func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { | |||
var rsa RawSockaddrAny | |||
var len _Socklen = SizeofSockaddrAny | |||
@@ -333,6 +358,10 @@ func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) { | |||
return setsockopt(fd, level, opt, unsafe.Pointer(tv), unsafe.Sizeof(*tv)) | |||
} | |||
func SetsockoptUint64(fd, level, opt int, value uint64) (err error) { | |||
return setsockopt(fd, level, opt, unsafe.Pointer(&value), 8) | |||
} | |||
func Socket(domain, typ, proto int) (fd int, err error) { | |||
if domain == AF_INET6 && SocketDisableIPv6 { | |||
return -1, EAFNOSUPPORT | |||
@@ -377,3 +406,22 @@ func SetNonblock(fd int, nonblocking bool) (err error) { | |||
func Exec(argv0 string, argv []string, envv []string) error { | |||
return syscall.Exec(argv0, argv, envv) | |||
} | |||
// Lutimes sets the access and modification times tv on path. If path refers to | |||
// a symlink, it is not dereferenced and the timestamps are set on the symlink. | |||
// If tv is nil, the access and modification times are set to the current time. | |||
// Otherwise tv must contain exactly 2 elements, with access time as the first | |||
// element and modification time as the second element. | |||
func Lutimes(path string, tv []Timeval) error { | |||
if tv == nil { | |||
return UtimesNanoAt(AT_FDCWD, path, nil, AT_SYMLINK_NOFOLLOW) | |||
} | |||
if len(tv) != 2 { | |||
return EINVAL | |||
} | |||
ts := []Timespec{ | |||
NsecToTimespec(TimevalToNsec(tv[0])), | |||
NsecToTimespec(TimevalToNsec(tv[1])), | |||
} | |||
return UtimesNanoAt(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW) | |||
} |
@@ -275,3 +275,9 @@ const ( | |||
// uname | |||
type Utsname C.struct_utsname | |||
// Clockinfo | |||
const SizeofClockinfo = C.sizeof_struct_clockinfo | |||
type Clockinfo C.struct_clockinfo |
@@ -274,3 +274,9 @@ type Utsname C.struct_utsname | |||
const SizeofUvmexp = C.sizeof_struct_uvmexp | |||
type Uvmexp C.struct_uvmexp | |||
// Clockinfo | |||
const SizeofClockinfo = C.sizeof_struct_clockinfo | |||
type Clockinfo C.struct_clockinfo |