Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5307a0ce6d | ||
|
|
9d779377cf | ||
|
|
8f580defa0 | ||
|
|
a4d1237429 | ||
|
|
b8d8dae13d | ||
|
|
f5a2eabcab | ||
|
|
44ecf3af99 | ||
|
|
ef5341b706 | ||
|
|
b999374650 | ||
|
|
c247dead11 | ||
|
|
1fda73153f | ||
|
|
1b4c3d2e8c | ||
|
|
b903b535d3 | ||
|
|
459a9db11b | ||
|
|
74e709ad8a | ||
|
|
b3790b8d91 | ||
|
|
1dc4269656 | ||
|
|
97bf787255 | ||
|
|
952517d181 | ||
|
|
c6fce02826 | ||
|
|
0ae49b8145 | ||
|
|
3bf9d2afd4 | ||
|
|
9bab96736c | ||
|
|
4f9f0ca9fc | ||
|
|
eac7cf0d78 | ||
|
|
18228cd6f1 | ||
|
|
73f6362471 | ||
|
|
ebc8e46315 | ||
|
|
e944286e33 | ||
|
|
78a1fd7893 | ||
|
|
ac58737d25 | ||
|
|
9dbbcf002b | ||
|
|
9f6bf8449a | ||
|
|
aae6e61070 | ||
|
|
9c1aa6af88 | ||
|
|
88199028d7 | ||
|
|
3f311e442e | ||
|
|
1f7c62cd66 | ||
|
|
a5f804889e | ||
|
|
958cde86ef | ||
|
|
51f005cfd4 | ||
|
|
7c358664da | ||
|
|
0091fc8e7c | ||
|
|
388684e50b | ||
|
|
953e809589 | ||
|
|
18f770732f | ||
|
|
2ebaafcdf5 | ||
|
|
4bc0711281 | ||
|
|
c96bba2ced | ||
|
|
6b13eef40b | ||
|
|
959f8f3db0 | ||
|
|
769bcd6997 | ||
|
|
d0a798f774 | ||
|
|
acbcbef23f | ||
|
|
376eb14006 | ||
|
|
b369b723c8 | ||
|
|
6b853fbea3 | ||
|
|
49bf5b80c8 | ||
|
|
24852b6b3f | ||
|
|
bbc689cf5c | ||
|
|
7292932d45 | ||
|
|
f66f74b0a4 | ||
|
|
b0784b7bfb | ||
|
|
911360c8a4 | ||
|
|
89ff08d67c | ||
|
|
e47973b1c1 | ||
|
|
9290511cd2 | ||
|
|
fa5273e461 | ||
|
|
a8ea4be81f | ||
|
|
71d3a4cfdb | ||
|
|
8929309228 | ||
|
|
4a75ba54c2 | ||
|
|
b4f1988a35 | ||
|
|
7042ebcbe0 | ||
|
|
3e90321ac7 | ||
|
|
8c4e668694 | ||
|
|
6018723c74 | ||
|
|
71ed71b4fa | ||
|
|
750a45fe5e | ||
|
|
36b172546b | ||
|
|
80ea76eb17 | ||
|
|
c17aa50fbd | ||
|
|
7cfb9161e8 | ||
|
|
b61b08db44 | ||
|
|
6c21748958 | ||
|
|
adef4cc1a8 | ||
|
|
a0819fbb02 | ||
|
|
42ee18b963 | ||
|
|
9e92970a1e | ||
|
|
c9da6b9a40 | ||
|
|
b35ab4fde0 | ||
|
|
bcb0f91bbc | ||
|
|
7eace71069 | ||
|
|
620dfbc770 | ||
|
|
82942cf1d8 | ||
|
|
0484c26df7 | ||
|
|
38ed1bc0ec | ||
|
|
38a0b5da75 | ||
|
|
bf5f14f545 | ||
|
|
b2d3a6a4b4 |
@@ -2,20 +2,17 @@
|
||||
|
||||
[](https://pkg.go.dev/golang.org/x/crypto)
|
||||
|
||||
This repository holds supplementary Go cryptography libraries.
|
||||
|
||||
## Download/Install
|
||||
|
||||
The easiest way to install is to run `go get -u golang.org/x/crypto/...`. You
|
||||
can also manually git clone the repository to `$GOPATH/src/golang.org/x/crypto`.
|
||||
This repository holds supplementary Go cryptography packages.
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://golang.org/doc/contribute.html.
|
||||
this repository, see https://go.dev/doc/contribute.
|
||||
|
||||
The git repository is https://go.googlesource.com/crypto.
|
||||
|
||||
The main issue tracker for the crypto repository is located at
|
||||
https://github.com/golang/go/issues. Prefix your issue with "x/crypto:" in the
|
||||
https://go.dev/issues. Prefix your issue with "x/crypto:" in the
|
||||
subject line, so it is easy to find.
|
||||
|
||||
Note that contributions to the cryptography package receive additional scrutiny
|
||||
|
||||
+45
-55
@@ -31,12 +31,11 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -353,6 +352,10 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization
|
||||
if _, err := c.Discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.dir.AuthzURL == "" {
|
||||
// Pre-Authorization is unsupported
|
||||
return nil, errPreAuthorizationNotSupported
|
||||
}
|
||||
|
||||
type authzID struct {
|
||||
Type string `json:"type"`
|
||||
@@ -467,7 +470,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
|
||||
// while waiting for a final authorization status.
|
||||
d := retryAfter(res.Header.Get("Retry-After"))
|
||||
if d == 0 {
|
||||
// Given that the fastest challenges TLS-SNI and HTTP-01
|
||||
// Given that the fastest challenges TLS-ALPN and HTTP-01
|
||||
// require a CA to make at least 1 network round trip
|
||||
// and most likely persist a challenge state,
|
||||
// this default delay seems reasonable.
|
||||
@@ -514,7 +517,11 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := c.post(ctx, nil, chal.URI, json.RawMessage("{}"), wantStatus(
|
||||
payload := json.RawMessage("{}")
|
||||
if len(chal.Payload) != 0 {
|
||||
payload = chal.Payload
|
||||
}
|
||||
res, err := c.post(ctx, nil, chal.URI, payload, wantStatus(
|
||||
http.StatusOK, // according to the spec
|
||||
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
||||
))
|
||||
@@ -564,50 +571,28 @@ func (c *Client) HTTP01ChallengePath(token string) string {
|
||||
}
|
||||
|
||||
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
|
||||
// Always returns an error.
|
||||
//
|
||||
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
|
||||
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||
ka, err := keyAuth(c.Key.Public(), token)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
}
|
||||
b := sha256.Sum256([]byte(ka))
|
||||
h := hex.EncodeToString(b[:])
|
||||
name = fmt.Sprintf("%s.%s.acme.invalid", h[:32], h[32:])
|
||||
cert, err = tlsChallengeCert([]string{name}, opt)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
}
|
||||
return cert, name, nil
|
||||
// Deprecated: This challenge type was only present in pre-standardized ACME
|
||||
// protocol drafts and is insecure for use in shared hosting environments.
|
||||
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) {
|
||||
return tls.Certificate{}, "", errPreRFC
|
||||
}
|
||||
|
||||
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
|
||||
// Always returns an error.
|
||||
//
|
||||
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
|
||||
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||
b := sha256.Sum256([]byte(token))
|
||||
h := hex.EncodeToString(b[:])
|
||||
sanA := fmt.Sprintf("%s.%s.token.acme.invalid", h[:32], h[32:])
|
||||
|
||||
ka, err := keyAuth(c.Key.Public(), token)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
}
|
||||
b = sha256.Sum256([]byte(ka))
|
||||
h = hex.EncodeToString(b[:])
|
||||
sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", h[:32], h[32:])
|
||||
|
||||
cert, err = tlsChallengeCert([]string{sanA, sanB}, opt)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
}
|
||||
return cert, sanA, nil
|
||||
// Deprecated: This challenge type was only present in pre-standardized ACME
|
||||
// protocol drafts and is insecure for use in shared hosting environments.
|
||||
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) {
|
||||
return tls.Certificate{}, "", errPreRFC
|
||||
}
|
||||
|
||||
// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
|
||||
// Servers can present the certificate to validate the challenge and prove control
|
||||
// over a domain name. For more details on TLS-ALPN-01 see
|
||||
// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
|
||||
// over an identifier (either a DNS name or the textual form of an IPv4 or IPv6
|
||||
// address). For more details on TLS-ALPN-01 see
|
||||
// https://www.rfc-editor.org/rfc/rfc8737 and https://www.rfc-editor.org/rfc/rfc8738
|
||||
//
|
||||
// The token argument is a Challenge.Token value.
|
||||
// If a WithKey option is provided, its private part signs the returned cert,
|
||||
@@ -615,9 +600,13 @@ func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||
//
|
||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||
// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
|
||||
// the server name in the TLS ClientHello matches the identifier, and the special acme-tls/1 ALPN protocol
|
||||
// has been specified.
|
||||
func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) {
|
||||
//
|
||||
// Validation requests for IP address identifiers will use the reverse DNS form in the server name
|
||||
// in the TLS ClientHello since the SNI extension is not supported for IP addresses.
|
||||
// See RFC 8738 Section 6 for more information.
|
||||
func (c *Client) TLSALPN01ChallengeCert(token, identifier string, opt ...CertOption) (cert tls.Certificate, err error) {
|
||||
ka, err := keyAuth(c.Key.Public(), token)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
@@ -647,7 +636,7 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
|
||||
}
|
||||
tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension)
|
||||
newOpt = append(newOpt, WithTemplate(tmpl))
|
||||
return tlsChallengeCert([]string{domain}, newOpt)
|
||||
return tlsChallengeCert(identifier, newOpt)
|
||||
}
|
||||
|
||||
// popNonce returns a nonce value previously stored with c.addNonce
|
||||
@@ -765,11 +754,15 @@ func defaultTLSChallengeCertTemplate() *x509.Certificate {
|
||||
}
|
||||
}
|
||||
|
||||
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
|
||||
// with the given SANs and auto-generated public/private key pair.
|
||||
// The Subject Common Name is set to the first SAN to aid debugging.
|
||||
// tlsChallengeCert creates a temporary certificate for TLS-ALPN challenges
|
||||
// for the given identifier, using an auto-generated public/private key pair.
|
||||
//
|
||||
// If the provided identifier is a domain name, it will be used as a DNS type SAN and for the
|
||||
// subject common name. If the provided identifier is an IP address it will be used as an IP type
|
||||
// SAN.
|
||||
//
|
||||
// To create a cert with a custom key pair, specify WithKey option.
|
||||
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||
func tlsChallengeCert(identifier string, opt []CertOption) (tls.Certificate, error) {
|
||||
var key crypto.Signer
|
||||
tmpl := defaultTLSChallengeCertTemplate()
|
||||
for _, o := range opt {
|
||||
@@ -793,9 +786,12 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
}
|
||||
tmpl.DNSNames = san
|
||||
if len(san) > 0 {
|
||||
tmpl.Subject.CommonName = san[0]
|
||||
|
||||
if ip := net.ParseIP(identifier); ip != nil {
|
||||
tmpl.IPAddresses = []net.IP{ip}
|
||||
} else {
|
||||
tmpl.DNSNames = []string{identifier}
|
||||
tmpl.Subject.CommonName = identifier
|
||||
}
|
||||
|
||||
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
|
||||
@@ -808,11 +804,5 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// encodePEM returns b encoded as PEM with block of type typ.
|
||||
func encodePEM(typ string, b []byte) []byte {
|
||||
pb := &pem.Block{Type: typ, Bytes: b}
|
||||
return pem.EncodeToMemory(pb)
|
||||
}
|
||||
|
||||
// timeNow is time.Now, except in tests which can mess with it.
|
||||
var timeNow = time.Now
|
||||
|
||||
+87
-111
@@ -9,20 +9,18 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -254,6 +252,57 @@ func TestAuthorizeValid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizeUnsupported(t *testing.T) {
|
||||
const (
|
||||
nonce = "https://example.com/acme/new-nonce"
|
||||
reg = "https://example.com/acme/new-acct"
|
||||
order = "https://example.com/acme/new-order"
|
||||
revoke = "https://example.com/acme/revoke-cert"
|
||||
keychange = "https://example.com/acme/key-change"
|
||||
metaTerms = "https://example.com/acme/terms/2017-5-30"
|
||||
metaWebsite = "https://www.example.com/"
|
||||
metaCAA = "example.com"
|
||||
)
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Replay-Nonce", "nonce")
|
||||
if r.Method == http.MethodHead {
|
||||
return
|
||||
}
|
||||
switch r.URL.Path {
|
||||
case "/": // Directory
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, `{
|
||||
"newNonce": %q,
|
||||
"newAccount": %q,
|
||||
"newOrder": %q,
|
||||
"revokeCert": %q,
|
||||
"keyChange": %q,
|
||||
"meta": {
|
||||
"termsOfService": %q,
|
||||
"website": %q,
|
||||
"caaIdentities": [%q],
|
||||
"externalAccountRequired": true
|
||||
}
|
||||
}`, nonce, reg, order, revoke, keychange, metaTerms, metaWebsite, metaCAA)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
case "/acme/new-authz":
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
client := &Client{Key: testKey, DirectoryURL: ts.URL}
|
||||
dir, err := client.Discover(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if dir.AuthzURL != "" {
|
||||
t.Fatalf("expected AuthzURL to be empty, got %q", dir.AuthzURL)
|
||||
}
|
||||
if _, err := client.Authorize(context.Background(), "example.com"); !errors.Is(err, errPreAuthorizationNotSupported) {
|
||||
t.Errorf("expected err to indicate pre-authorization is unsupported, got %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitAuthorization(t *testing.T) {
|
||||
t.Run("wait loop", func(t *testing.T) {
|
||||
var count int
|
||||
@@ -640,71 +689,6 @@ func TestLinkHeader(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSSNI01ChallengeCert(t *testing.T) {
|
||||
const (
|
||||
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
|
||||
// echo -n <token.testKeyECThumbprint> | shasum -a 256
|
||||
san = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.acme.invalid"
|
||||
)
|
||||
|
||||
tlscert, name, err := newTestClient().TLSSNI01ChallengeCert(token)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n := len(tlscert.Certificate); n != 1 {
|
||||
t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
|
||||
}
|
||||
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cert.DNSNames) != 1 || cert.DNSNames[0] != san {
|
||||
t.Fatalf("cert.DNSNames = %v; want %q", cert.DNSNames, san)
|
||||
}
|
||||
if cert.DNSNames[0] != name {
|
||||
t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
|
||||
}
|
||||
if cn := cert.Subject.CommonName; cn != san {
|
||||
t.Errorf("cert.Subject.CommonName = %q; want %q", cn, san)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSSNI02ChallengeCert(t *testing.T) {
|
||||
const (
|
||||
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
|
||||
// echo -n evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA | shasum -a 256
|
||||
sanA = "7ea0aaa69214e71e02cebb18bb867736.09b730209baabf60e43d4999979ff139.token.acme.invalid"
|
||||
// echo -n <token.testKeyECThumbprint> | shasum -a 256
|
||||
sanB = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.ka.acme.invalid"
|
||||
)
|
||||
|
||||
tlscert, name, err := newTestClient().TLSSNI02ChallengeCert(token)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n := len(tlscert.Certificate); n != 1 {
|
||||
t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
|
||||
}
|
||||
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
names := []string{sanA, sanB}
|
||||
if !reflect.DeepEqual(cert.DNSNames, names) {
|
||||
t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
|
||||
}
|
||||
sort.Strings(cert.DNSNames)
|
||||
i := sort.SearchStrings(cert.DNSNames, name)
|
||||
if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
|
||||
t.Errorf("%v doesn't have %q", cert.DNSNames, name)
|
||||
}
|
||||
if cn := cert.Subject.CommonName; cn != sanA {
|
||||
t.Errorf("CommonName = %q; want %q", cn, sanA)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSALPN01ChallengeCert(t *testing.T) {
|
||||
const (
|
||||
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
|
||||
@@ -757,10 +741,11 @@ func TestTLSALPN01ChallengeCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTLSChallengeCertOpt(t *testing.T) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 512)
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
domain := "example.com"
|
||||
tmpl := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(2),
|
||||
Subject: pkix.Name{Organization: []string{"Test"}},
|
||||
@@ -769,52 +754,43 @@ func TestTLSChallengeCertOpt(t *testing.T) {
|
||||
opts := []CertOption{WithKey(key), WithTemplate(tmpl)}
|
||||
|
||||
client := newTestClient()
|
||||
cert1, _, err := client.TLSSNI01ChallengeCert("token", opts...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cert2, _, err := client.TLSSNI02ChallengeCert("token", opts...)
|
||||
cert, err := client.TLSALPN01ChallengeCert("token", domain, opts...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, tlscert := range []tls.Certificate{cert1, cert2} {
|
||||
// verify generated cert private key
|
||||
tlskey, ok := tlscert.PrivateKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
t.Errorf("%d: tlscert.PrivateKey is %T; want *rsa.PrivateKey", i, tlscert.PrivateKey)
|
||||
continue
|
||||
}
|
||||
if tlskey.D.Cmp(key.D) != 0 {
|
||||
t.Errorf("%d: tlskey.D = %v; want %v", i, tlskey.D, key.D)
|
||||
}
|
||||
// verify generated cert public key
|
||||
x509Cert, err := x509.ParseCertificate(tlscert.Certificate[0])
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
t.Errorf("%d: x509Cert.PublicKey is %T; want *rsa.PublicKey", i, x509Cert.PublicKey)
|
||||
continue
|
||||
}
|
||||
if tlspub.N.Cmp(key.N) != 0 {
|
||||
t.Errorf("%d: tlspub.N = %v; want %v", i, tlspub.N, key.N)
|
||||
}
|
||||
// verify template option
|
||||
sn := big.NewInt(2)
|
||||
if x509Cert.SerialNumber.Cmp(sn) != 0 {
|
||||
t.Errorf("%d: SerialNumber = %v; want %v", i, x509Cert.SerialNumber, sn)
|
||||
}
|
||||
org := []string{"Test"}
|
||||
if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
|
||||
t.Errorf("%d: Subject.Organization = %+v; want %+v", i, x509Cert.Subject.Organization, org)
|
||||
}
|
||||
for _, v := range x509Cert.DNSNames {
|
||||
if !strings.HasSuffix(v, ".acme.invalid") {
|
||||
t.Errorf("%d: invalid DNSNames element: %q", i, v)
|
||||
}
|
||||
// verify generated cert private key
|
||||
tlskey, ok := cert.PrivateKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
t.Fatalf("tlscert.PrivateKey is %T; want *rsa.PrivateKey", cert.PrivateKey)
|
||||
}
|
||||
if tlskey.D.Cmp(key.D) != 0 {
|
||||
t.Errorf("tlskey.D = %v; want %v", tlskey.D, key.D)
|
||||
}
|
||||
// verify generated cert public key
|
||||
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
t.Fatalf("x509Cert.PublicKey is %T; want *rsa.PublicKey", x509Cert.PublicKey)
|
||||
}
|
||||
if tlspub.N.Cmp(key.N) != 0 {
|
||||
t.Errorf("tlspub.N = %v; want %v", tlspub.N, key.N)
|
||||
}
|
||||
// verify template option
|
||||
sn := big.NewInt(2)
|
||||
if x509Cert.SerialNumber.Cmp(sn) != 0 {
|
||||
t.Errorf("SerialNumber = %v; want %v", x509Cert.SerialNumber, sn)
|
||||
}
|
||||
org := []string{"Test"}
|
||||
if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
|
||||
t.Errorf("Subject.Organization = %+v; want %+v", x509Cert.Subject.Organization, org)
|
||||
}
|
||||
for _, v := range x509Cert.DNSNames {
|
||||
if v != domain {
|
||||
t.Errorf("invalid DNSNames element: %q", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,6 +292,10 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||
}
|
||||
|
||||
// regular domain
|
||||
if err := m.hostPolicy()(ctx, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ck := certKey{
|
||||
domain: strings.TrimSuffix(name, "."), // golang.org/issue/18114
|
||||
isRSA: !supportsECDSA(hello),
|
||||
@@ -305,9 +309,6 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||
}
|
||||
|
||||
// first-time
|
||||
if err := m.hostPolicy()(ctx, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err = m.createCert(ctx, ck)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -619,7 +619,7 @@ func TestCache(t *testing.T) {
|
||||
PrivateKey: ecdsaKey,
|
||||
}
|
||||
|
||||
rsaKey, err := rsa.GenerateKey(rand.Reader, 512)
|
||||
rsaKey, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -694,7 +694,7 @@ func TestValidCert(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key3, err := rsa.GenerateKey(rand.Reader, 512)
|
||||
key3, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := decodePayload(&req, r.Body); err != nil {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
|
||||
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
Identifiers []struct{ Value string }
|
||||
}
|
||||
if err := decodePayload(&req, r.Body); err != nil {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
|
||||
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
|
||||
return
|
||||
}
|
||||
ca.mu.Lock()
|
||||
@@ -352,7 +352,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
defer ca.mu.Unlock()
|
||||
o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/orders/"))
|
||||
if err != nil {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
|
||||
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
|
||||
return
|
||||
}
|
||||
if err := json.NewEncoder(w).Encode(o); err != nil {
|
||||
@@ -412,7 +412,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
orderID := strings.TrimPrefix(r.URL.Path, "/new-cert/")
|
||||
o, err := ca.storedOrder(orderID)
|
||||
if err != nil {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
|
||||
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
|
||||
return
|
||||
}
|
||||
if o.Status != acme.StatusReady {
|
||||
@@ -427,7 +427,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
|
||||
csr, err := x509.ParseCertificateRequest(b)
|
||||
if err != nil {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
|
||||
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
|
||||
return
|
||||
}
|
||||
// Issue the certificate.
|
||||
@@ -449,7 +449,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
||||
defer ca.mu.Unlock()
|
||||
o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/issued-cert/"))
|
||||
if err != nil {
|
||||
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
|
||||
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
|
||||
return
|
||||
}
|
||||
if o.Status != acme.StatusValid {
|
||||
@@ -782,15 +782,3 @@ func decodePayload(v interface{}, r io.Reader) error {
|
||||
func challengeToken(domain, challType string, authzID int) string {
|
||||
return fmt.Sprintf("token-%s-%s-%d", domain, challType, authzID)
|
||||
}
|
||||
|
||||
func unique(a []string) []string {
|
||||
seen := make(map[string]bool)
|
||||
var res []string
|
||||
for _, s := range a {
|
||||
if s != "" && !seen[s] {
|
||||
seen[s] = true
|
||||
res = append(res, s)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -124,32 +123,13 @@ func (ln *listener) Close() error {
|
||||
return ln.tcpListener.Close()
|
||||
}
|
||||
|
||||
func homeDir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||
}
|
||||
if h := os.Getenv("HOME"); h != "" {
|
||||
return h
|
||||
}
|
||||
return "/"
|
||||
}
|
||||
|
||||
func cacheDir() string {
|
||||
const base = "golang-autocert"
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return filepath.Join(homeDir(), "Library", "Caches", base)
|
||||
case "windows":
|
||||
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
|
||||
if v := os.Getenv(ev); v != "" {
|
||||
return filepath.Join(v, base)
|
||||
}
|
||||
}
|
||||
// Worst case:
|
||||
return filepath.Join(homeDir(), base)
|
||||
cache, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
// Fall back to the root directory.
|
||||
cache = "/.cache"
|
||||
}
|
||||
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
|
||||
return filepath.Join(xdg, base)
|
||||
}
|
||||
return filepath.Join(homeDir(), ".cache", base)
|
||||
|
||||
return filepath.Join(cache, base)
|
||||
}
|
||||
|
||||
+2
-5
@@ -66,7 +66,7 @@ func (c *Client) retryTimer() *retryTimer {
|
||||
// The n argument is always bounded between 1 and 30.
|
||||
// The returned value is always greater than 0.
|
||||
func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
|
||||
const max = 10 * time.Second
|
||||
const maxVal = 10 * time.Second
|
||||
var jitter time.Duration
|
||||
if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
|
||||
// Set the minimum to 1ms to avoid a case where
|
||||
@@ -86,10 +86,7 @@ func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
|
||||
n = 30
|
||||
}
|
||||
d := time.Duration(1<<uint(n-1))*time.Second + jitter
|
||||
if d > max {
|
||||
return max
|
||||
}
|
||||
return d
|
||||
return min(d, maxVal)
|
||||
}
|
||||
|
||||
// retryAfter parses a Retry-After HTTP header value,
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, ur
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
phead := base64.RawURLEncoding.EncodeToString([]byte(phJSON))
|
||||
phead := base64.RawURLEncoding.EncodeToString(phJSON)
|
||||
var payload string
|
||||
if val, ok := claimset.(string); ok {
|
||||
payload = val
|
||||
|
||||
@@ -0,0 +1,809 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package acme_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/acme"
|
||||
)
|
||||
|
||||
const (
|
||||
// pebbleModVersion is the module version used for Pebble and Pebble's
|
||||
// challenge test server. It is ignored if `-pebble-local-dir` is provided.
|
||||
pebbleModVersion = "v2.7.0"
|
||||
// startingPort is the first port number used for binding interface
|
||||
// addresses. Each call to takeNextPort() will increment a port number
|
||||
// starting at this value.
|
||||
startingPort = 5555
|
||||
)
|
||||
|
||||
var (
|
||||
pebbleLocalDir = flag.String(
|
||||
"pebble-local-dir",
|
||||
"",
|
||||
"Local Pebble to use, instead of fetching from source",
|
||||
)
|
||||
nextPort atomic.Uint32
|
||||
)
|
||||
|
||||
func init() {
|
||||
nextPort.Store(startingPort)
|
||||
}
|
||||
|
||||
func TestWithPebble(t *testing.T) {
|
||||
// We want to use process groups w/ syscall.Kill, and the acme package
|
||||
// is very platform-agnostic, so skip on non-Linux.
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("skipping pebble tests on non-linux OS")
|
||||
}
|
||||
|
||||
if testing.Short() {
|
||||
t.Skip("skipping pebble tests in short mode")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
challSrv func(*environment) (challengeServer, string)
|
||||
}{
|
||||
{
|
||||
name: "TLSALPN01-Issuance",
|
||||
challSrv: func(env *environment) (challengeServer, string) {
|
||||
bindAddr := fmt.Sprintf(":%d", env.config.TLSPort)
|
||||
return newChallTLSServer(bindAddr), bindAddr
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "HTTP01-Issuance",
|
||||
challSrv: func(env *environment) (challengeServer, string) {
|
||||
bindAddr := fmt.Sprintf(":%d", env.config.HTTPPort)
|
||||
return newChallHTTPServer(bindAddr), bindAddr
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
env := startPebbleEnvironment(t, nil)
|
||||
challSrv, challSrvAddr := tt.challSrv(&env)
|
||||
challSrv.Run()
|
||||
|
||||
t.Cleanup(func() {
|
||||
challSrv.Shutdown()
|
||||
})
|
||||
|
||||
waitForServer(t, challSrvAddr)
|
||||
testIssuance(t, &env, challSrv)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// challengeServer abstracts over the details of running a challenge response
|
||||
// server for some supported acme.Challenge type. Responses are provisioned
|
||||
// during the test issuance process to be presented to the ACME server's
|
||||
// validation authority.
|
||||
type challengeServer interface {
|
||||
Run()
|
||||
Shutdown() error
|
||||
Supported(chal *acme.Challenge) bool
|
||||
Provision(client *acme.Client, ident acme.AuthzID, chal *acme.Challenge) error
|
||||
}
|
||||
|
||||
// challTLSServer is a simple challenge response server that listens for TLS
|
||||
// connections on a specific port and if they are TLS-ALPN-01 challenge
|
||||
// requests, completes the handshake using the configured challenge response
|
||||
// certificate for the SNI value provided.
|
||||
type challTLSServer struct {
|
||||
*http.Server
|
||||
// mu protects challCerts.
|
||||
mu sync.RWMutex
|
||||
// challCerts is a map from SNI domain name to challenge response certificate.
|
||||
challCerts map[string]*tls.Certificate
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8737#section-4
|
||||
const acmeTLSAlpnProtocol = "acme-tls/1"
|
||||
|
||||
func newChallTLSServer(address string) *challTLSServer {
|
||||
challServer := &challTLSServer{Server: &http.Server{
|
||||
Addr: address,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
}, challCerts: make(map[string]*tls.Certificate)}
|
||||
|
||||
// Configure the server to support the TLS-ALPN-01 challenge protocol
|
||||
// and to use a callback for selecting the handshake certificate.
|
||||
challServer.Server.TLSConfig = &tls.Config{
|
||||
NextProtos: []string{acmeTLSAlpnProtocol},
|
||||
GetCertificate: challServer.getCertificate,
|
||||
}
|
||||
|
||||
return challServer
|
||||
}
|
||||
|
||||
func (c *challTLSServer) Shutdown() error {
|
||||
log.Printf("challTLSServer: shutting down")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10)
|
||||
defer cancel()
|
||||
return c.Server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
func (c *challTLSServer) Run() {
|
||||
go func() {
|
||||
// Note: certFile and keyFile are empty because our config uses a
|
||||
// GetCertificate callback.
|
||||
if err := c.Server.ListenAndServeTLS("", ""); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Printf("challTLSServer error: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *challTLSServer) Supported(chal *acme.Challenge) bool {
|
||||
return chal.Type == "tls-alpn-01"
|
||||
}
|
||||
|
||||
func (c *challTLSServer) Provision(client *acme.Client, ident acme.AuthzID, chal *acme.Challenge) error {
|
||||
respCert, err := client.TLSALPN01ChallengeCert(chal.Token, ident.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("challTLSServer: failed to generate challlenge response cert for %s: %w",
|
||||
ident.Value, err)
|
||||
}
|
||||
|
||||
log.Printf("challTLSServer: setting challenge response certificate for %s", ident.Value)
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.challCerts[ident.Value] = &respCert
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *challTLSServer) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
// Verify the request looks like a TLS-ALPN-01 challenge request.
|
||||
if len(clientHello.SupportedProtos) != 1 || clientHello.SupportedProtos[0] != acmeTLSAlpnProtocol {
|
||||
return nil, fmt.Errorf(
|
||||
"challTLSServer: non-TLS-ALPN-01 challenge request received with SupportedProtos: %s",
|
||||
clientHello.SupportedProtos)
|
||||
}
|
||||
|
||||
serverName := clientHello.ServerName
|
||||
|
||||
// TLS-ALPN-01 challenge requests for IP addresses are encoded in the SNI
|
||||
// using the reverse-DNS notation. See RFC 8738 Section 6:
|
||||
// https://www.rfc-editor.org/rfc/rfc8738.html#section-6
|
||||
if strings.HasSuffix(serverName, ".in-addr.arpa") {
|
||||
serverName = strings.TrimSuffix(serverName, ".in-addr.arpa")
|
||||
parts := strings.Split(serverName, ".")
|
||||
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
|
||||
parts[i], parts[j] = parts[j], parts[i]
|
||||
}
|
||||
serverName = strings.Join(parts, ".")
|
||||
}
|
||||
|
||||
log.Printf("challTLSServer: selecting certificate for request from %s for %s",
|
||||
clientHello.Conn.RemoteAddr(), serverName)
|
||||
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
cert := c.challCerts[serverName]
|
||||
if cert == nil {
|
||||
return nil, fmt.Errorf("challTLSServer: no challenge response certificate configured for %s", serverName)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// challHTTPServer is a simple challenge response server that listens for HTTP
|
||||
// connections on a specific port and if they are HTTP-01 challenge requests,
|
||||
// serves the challenge response key authorization.
|
||||
type challHTTPServer struct {
|
||||
*http.Server
|
||||
// mu protects challMap
|
||||
mu sync.RWMutex
|
||||
// challMap is a mapping from request path to response body.
|
||||
challMap map[string]string
|
||||
}
|
||||
|
||||
func newChallHTTPServer(address string) *challHTTPServer {
|
||||
challServer := &challHTTPServer{
|
||||
Server: &http.Server{
|
||||
Addr: address,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
},
|
||||
challMap: make(map[string]string),
|
||||
}
|
||||
|
||||
challServer.Server.Handler = challServer
|
||||
|
||||
return challServer
|
||||
}
|
||||
|
||||
func (c *challHTTPServer) Supported(chal *acme.Challenge) bool {
|
||||
return chal.Type == "http-01"
|
||||
}
|
||||
|
||||
func (c *challHTTPServer) Provision(client *acme.Client, ident acme.AuthzID, chall *acme.Challenge) error {
|
||||
path := client.HTTP01ChallengePath(chall.Token)
|
||||
body, err := client.HTTP01ChallengeResponse(chall.Token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate HTTP-01 challenge response for %v challenge %s token %s: %w",
|
||||
ident, chall.URI, chall.Token, err)
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
log.Printf("challHTTPServer: setting challenge response for %s", path)
|
||||
c.challMap[path] = body
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *challHTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("challHTTPServer: handling %s to %s from %s", r.Method, r.URL.Path, r.RemoteAddr)
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
response, exists := c.challMap[r.URL.Path]
|
||||
|
||||
if !exists {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(response))
|
||||
}
|
||||
|
||||
func (c *challHTTPServer) Shutdown() error {
|
||||
log.Printf("challHTTPServer: shutting down")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
return c.Server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
func (c *challHTTPServer) Run() {
|
||||
go func() {
|
||||
if err := c.Server.ListenAndServe(); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Printf("challHTTPServer error: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func testIssuance(t *testing.T, env *environment, challSrv challengeServer) {
|
||||
t.Helper()
|
||||
|
||||
// Bound the total issuance process by a timeout of 60 seconds.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Create a new ACME account.
|
||||
client := env.client
|
||||
acct, err := client.Register(ctx, &acme.Account{}, acme.AcceptTOS)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to register account: %v", err)
|
||||
}
|
||||
if acct.Status != acme.StatusValid {
|
||||
t.Fatalf("expected new account status to be valid, got %v", acct.Status)
|
||||
}
|
||||
log.Printf("registered account: %s", acct.URI)
|
||||
|
||||
// Create a new order for some example identifiers
|
||||
identifiers := []acme.AuthzID{
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "example.com",
|
||||
},
|
||||
{
|
||||
Type: "dns",
|
||||
Value: "www.example.com",
|
||||
},
|
||||
{
|
||||
Type: "ip",
|
||||
Value: "127.0.0.1",
|
||||
},
|
||||
}
|
||||
order, err := client.AuthorizeOrder(ctx, identifiers)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create order for %v: %v", identifiers, err)
|
||||
}
|
||||
if order.Status != acme.StatusPending {
|
||||
t.Fatalf("expected new order status to be pending, got %v", order.Status)
|
||||
}
|
||||
orderURL := order.URI
|
||||
log.Printf("created order: %v", orderURL)
|
||||
|
||||
// For each pending authz provision a supported challenge type's response
|
||||
// with the test challenge server, and tell the ACME server to verify it.
|
||||
for _, authzURL := range order.AuthzURLs {
|
||||
authz, err := client.GetAuthorization(ctx, authzURL)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get order %s authorization %s: %v",
|
||||
orderURL, authzURL, err)
|
||||
}
|
||||
|
||||
if authz.Status != acme.StatusPending {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, challenge := range authz.Challenges {
|
||||
if challenge.Status != acme.StatusPending || !challSrv.Supported(challenge) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := challSrv.Provision(client, authz.Identifier, challenge); err != nil {
|
||||
t.Fatalf("failed to provision challenge %s: %v", challenge.URI, err)
|
||||
}
|
||||
|
||||
_, err = client.Accept(ctx, challenge)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to accept order %s challenge %s: %v",
|
||||
orderURL, challenge.URI, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the order to become ready for finalization.
|
||||
order, err = client.WaitOrder(ctx, order.URI)
|
||||
if err != nil {
|
||||
var orderErr *acme.OrderError
|
||||
if errors.Is(err, orderErr) {
|
||||
t.Fatalf("failed to wait for order %s: %s: %s", orderURL, err, orderErr.Problem)
|
||||
} else {
|
||||
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
|
||||
}
|
||||
}
|
||||
if order.Status != acme.StatusReady {
|
||||
t.Fatalf("expected order %s status to be ready, got %v",
|
||||
orderURL, order.Status)
|
||||
}
|
||||
|
||||
// Generate a certificate keypair and a CSR for the order identifiers.
|
||||
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate certificate key: %v", err)
|
||||
}
|
||||
var dnsNames []string
|
||||
var ipAddresses []net.IP
|
||||
for _, ident := range identifiers {
|
||||
switch ident.Type {
|
||||
case "dns":
|
||||
dnsNames = append(dnsNames, ident.Value)
|
||||
case "ip":
|
||||
ipAddresses = append(ipAddresses, net.ParseIP(ident.Value))
|
||||
default:
|
||||
t.Fatalf("unsupported identifier type: %s", ident.Type)
|
||||
}
|
||||
}
|
||||
csrDer, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
|
||||
DNSNames: dnsNames,
|
||||
IPAddresses: ipAddresses,
|
||||
}, certKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create CSR: %v", err)
|
||||
}
|
||||
|
||||
// Finalize the order by creating a certificate with our CSR.
|
||||
chain, _, err := client.CreateOrderCert(ctx, order.FinalizeURL, csrDer, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to finalize order %s with finalize URL %s: %v",
|
||||
orderURL, order.FinalizeURL, err)
|
||||
}
|
||||
|
||||
// Split the chain into the leaf and any intermediates.
|
||||
leaf := chain[0]
|
||||
intermediatesDER := chain[1:]
|
||||
leafCert, err := x509.ParseCertificate(leaf)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse order %s leaf certificate: %v", orderURL, err)
|
||||
}
|
||||
intermediates := x509.NewCertPool()
|
||||
for i, intermediateDER := range intermediatesDER {
|
||||
intermediate, err := x509.ParseCertificate(intermediateDER)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse intermediate %d: %v", i, err)
|
||||
}
|
||||
intermediates.AddCert(intermediate)
|
||||
}
|
||||
|
||||
// Verify there is a valid path from the leaf certificate to Pebble's
|
||||
// issuing root using the provided intermediate certificates.
|
||||
roots, err := env.RootCert()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get Pebble issuer root certs: %v", err)
|
||||
}
|
||||
paths, err := leafCert.Verify(x509.VerifyOptions{
|
||||
Intermediates: intermediates,
|
||||
Roots: roots,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to verify order %s leaf certificate: %v", orderURL, err)
|
||||
}
|
||||
log.Printf("verified %d path(s) from issued leaf certificate to Pebble root CA", len(paths))
|
||||
|
||||
// Also verify that the leaf cert is valid for each of the DNS names
|
||||
// and IP addresses from our order's identifiers.
|
||||
for _, name := range dnsNames {
|
||||
if err := leafCert.VerifyHostname(name); err != nil {
|
||||
t.Fatalf("failed to verify order %s leaf certificate for order DNS name %s: %v",
|
||||
orderURL, name, err)
|
||||
}
|
||||
}
|
||||
for _, ip := range ipAddresses {
|
||||
if err := leafCert.VerifyHostname(ip.String()); err != nil {
|
||||
t.Fatalf("failed to verify order %s leaf certificate for order IP address %s: %v",
|
||||
orderURL, ip, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type environment struct {
|
||||
config *environmentConfig
|
||||
client *acme.Client
|
||||
}
|
||||
|
||||
// RootCert returns the Pebble CA's primary issuing hierarchy root certificate.
|
||||
// This is generated randomly at each startup and can be used to verify
|
||||
// certificate chains issued by Pebble's ACME interface. Note that this
|
||||
// is separate from the static root certificate used by the Pebble ACME
|
||||
// HTTPS interface.
|
||||
func (e *environment) RootCert() (*x509.CertPool, error) {
|
||||
// NOTE: in the future we may want to consider the alternative chains
|
||||
// returned as Link alternative headers.
|
||||
rootURL := fmt.Sprintf("https://%s/roots/0", e.config.pebbleConfig.ManagementListenAddress)
|
||||
resp, err := e.client.HTTPClient.Get(rootURL)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to GET Pebble root CA from %s: %v", rootURL, err)
|
||||
}
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
rootPEM, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Pebble root CA PEM: %v", err)
|
||||
}
|
||||
rootDERBlock, _ := pem.Decode(rootPEM)
|
||||
rootCA, err := x509.ParseCertificate(rootDERBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Pebble root CA DER: %v", err)
|
||||
}
|
||||
roots.AddCert(rootCA)
|
||||
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
// environmentConfig describes the Pebble configuration, and configuration
|
||||
// shared between pebble and pebble-challtestsrv.
|
||||
type environmentConfig struct {
|
||||
pebbleConfig
|
||||
dnsPort uint32
|
||||
}
|
||||
|
||||
// defaultConfig returns an environmentConfig populated with default values.
|
||||
// The provided pebbleDir is used to specify certificate/private key paths
|
||||
// for the HTTPS ACME interface.
|
||||
func defaultConfig(pebbleDir string) environmentConfig {
|
||||
return environmentConfig{
|
||||
pebbleConfig: pebbleConfig{
|
||||
ListenAddress: fmt.Sprintf("127.0.0.1:%d", takeNextPort()),
|
||||
ManagementListenAddress: fmt.Sprintf("127.0.0.1:%d", takeNextPort()),
|
||||
HTTPPort: takeNextPort(),
|
||||
TLSPort: takeNextPort(),
|
||||
Certificate: fmt.Sprintf("%s/test/certs/localhost/cert.pem", pebbleDir),
|
||||
PrivateKey: fmt.Sprintf("%s/test/certs/localhost/key.pem", pebbleDir),
|
||||
OCSPResponderURL: "",
|
||||
ExternalAccountBindingRequired: false,
|
||||
ExternalAccountMACKeys: make(map[string]string),
|
||||
DomainBlocklist: []string{"blocked-domain.example"},
|
||||
Profiles: map[string]struct {
|
||||
Description string
|
||||
ValidityPeriod uint64
|
||||
}{
|
||||
"default": {
|
||||
Description: "default profile",
|
||||
ValidityPeriod: 3600,
|
||||
},
|
||||
},
|
||||
RetryAfter: struct {
|
||||
Authz int
|
||||
Order int
|
||||
}{
|
||||
3,
|
||||
5,
|
||||
},
|
||||
},
|
||||
dnsPort: takeNextPort(),
|
||||
}
|
||||
}
|
||||
|
||||
// pebbleConfig matches the JSON structure of the Pebble configuration file.
|
||||
type pebbleConfig struct {
|
||||
ListenAddress string
|
||||
ManagementListenAddress string
|
||||
HTTPPort uint32
|
||||
TLSPort uint32
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
OCSPResponderURL string
|
||||
ExternalAccountBindingRequired bool
|
||||
ExternalAccountMACKeys map[string]string
|
||||
DomainBlocklist []string
|
||||
Profiles map[string]struct {
|
||||
Description string
|
||||
ValidityPeriod uint64
|
||||
}
|
||||
RetryAfter struct {
|
||||
Authz int
|
||||
Order int
|
||||
}
|
||||
}
|
||||
|
||||
func takeNextPort() uint32 {
|
||||
return nextPort.Add(1) - 1
|
||||
}
|
||||
|
||||
// startPebbleEnvironment is a test helper that spawns Pebble and Pebble
|
||||
// challenge test server processes based on the provided environmentConfig. The
|
||||
// processes will be torn down when the test ends.
|
||||
func startPebbleEnvironment(t *testing.T, config *environmentConfig) environment {
|
||||
t.Helper()
|
||||
|
||||
var pebbleDir string
|
||||
if *pebbleLocalDir != "" {
|
||||
pebbleDir = *pebbleLocalDir
|
||||
} else {
|
||||
pebbleDir = fetchModule(t, "github.com/letsencrypt/pebble/v2", pebbleModVersion)
|
||||
}
|
||||
|
||||
binDir := prepareBinaries(t, pebbleDir)
|
||||
|
||||
if config == nil {
|
||||
cfg := defaultConfig(pebbleDir)
|
||||
config = &cfg
|
||||
}
|
||||
|
||||
marshalConfig := struct {
|
||||
Pebble pebbleConfig
|
||||
}{
|
||||
Pebble: config.pebbleConfig,
|
||||
}
|
||||
|
||||
configData, err := json.Marshal(marshalConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal config: %v", err)
|
||||
}
|
||||
|
||||
configFile, err := os.CreateTemp("", "pebble-config-*.json")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp config file: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { os.Remove(configFile.Name()) })
|
||||
|
||||
if _, err := configFile.Write(configData); err != nil {
|
||||
t.Fatalf("failed to write config file: %v", err)
|
||||
}
|
||||
configFile.Close()
|
||||
|
||||
log.Printf("pebble dir: %s", pebbleDir)
|
||||
log.Printf("config file: %s", configFile.Name())
|
||||
|
||||
// Spawn the Pebble CA server. It answers ACME requests and performs
|
||||
// outbound validations. We configure it to use a mock DNS server that
|
||||
// always answers 127.0.0.1 for all A queries so that validation
|
||||
// requests for any domain name will resolve to our local challenge
|
||||
// server instances.
|
||||
spawnServerProcess(t, binDir, "pebble", "-config", configFile.Name(),
|
||||
"-dnsserver", fmt.Sprintf("127.0.0.1:%d", config.dnsPort),
|
||||
"-strict")
|
||||
|
||||
// Spawn the Pebble challenge test server. We'll use it to mock DNS
|
||||
// responses but disable all the other interfaces. We want to stand
|
||||
// up our own challenge response servers for TLS-ALPN-01,
|
||||
// etc.
|
||||
// Note: we specify -defaultIPv6 "" so that no AAAA records are served.
|
||||
// The LUCI CI runners have issues with IPv6 connectivity on localhost.
|
||||
spawnServerProcess(t, binDir, "pebble-challtestsrv",
|
||||
"-dns01", fmt.Sprintf(":%d", config.dnsPort),
|
||||
"-defaultIPv6", "",
|
||||
"-management", fmt.Sprintf(":%d", takeNextPort()),
|
||||
"-doh", "",
|
||||
"-http01", "",
|
||||
"-tlsalpn01", "",
|
||||
"-https01", "")
|
||||
|
||||
waitForServer(t, config.pebbleConfig.ListenAddress)
|
||||
waitForServer(t, fmt.Sprintf("127.0.0.1:%d", config.dnsPort))
|
||||
|
||||
log.Printf("pebble environment ready")
|
||||
|
||||
// Construct a cert pool that contains the CA certificate used by the ACME
|
||||
// interface's certificate chain. This is separate from the issuing
|
||||
// hierarchy and is used for the ACME client to interact with the ACME
|
||||
// interface without cert verification error.
|
||||
caCertPath := filepath.Join(pebbleDir, "test/certs/pebble.minica.pem")
|
||||
caCert, err := os.ReadFile(caCertPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read CA certificate %s: %v", caCertPath, err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
t.Fatalf("failed to parse CA certificate %s", caCertPath)
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: caCertPool,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create an ACME account keypair/client and verify it can discover
|
||||
// the Pebble server's ACME directory without error.
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate account key: %v", err)
|
||||
}
|
||||
client := &acme.Client{
|
||||
Key: key,
|
||||
HTTPClient: httpClient,
|
||||
DirectoryURL: fmt.Sprintf("https://%s/dir", config.ListenAddress),
|
||||
}
|
||||
_, err = client.Discover(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to discover ACME directory: %v", err)
|
||||
}
|
||||
|
||||
return environment{
|
||||
config: config,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func waitForServer(t *testing.T, addr string) {
|
||||
t.Helper()
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
if conn, err := net.Dial("tcp", addr); err == nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Duration(i*100) * time.Millisecond)
|
||||
}
|
||||
t.Fatalf("failed to connect to %q after 20 tries", addr)
|
||||
}
|
||||
|
||||
// fetchModule fetches the module at the given version and returns the directory
|
||||
// containing its source tree. It skips the test if fetching modules is not
|
||||
// possible in this environment.
|
||||
//
|
||||
// Copied from the stdlib cryptotest.FetchModule and adapted to not rely on the
|
||||
// stdlib internal testenv package.
|
||||
func fetchModule(t *testing.T, module, version string) string {
|
||||
// If the default GOMODCACHE doesn't exist, use a temporary directory
|
||||
// instead. (For example, run.bash sets GOPATH=/nonexist-gopath.)
|
||||
out, err := exec.Command("go", "env", "GOMODCACHE").Output()
|
||||
if err != nil {
|
||||
t.Errorf("go env GOMODCACHE: %v\n%s", err, out)
|
||||
if ee, ok := err.(*exec.ExitError); ok {
|
||||
t.Logf("%s", ee.Stderr)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
modcacheOk := false
|
||||
if gomodcache := string(bytes.TrimSpace(out)); gomodcache != "" {
|
||||
if _, err := os.Stat(gomodcache); err == nil {
|
||||
modcacheOk = true
|
||||
}
|
||||
}
|
||||
if !modcacheOk {
|
||||
t.Setenv("GOMODCACHE", t.TempDir())
|
||||
// Allow t.TempDir() to clean up subdirectories.
|
||||
t.Setenv("GOFLAGS", os.Getenv("GOFLAGS")+" -modcacherw")
|
||||
}
|
||||
|
||||
t.Logf("fetching %s@%s\n", module, version)
|
||||
|
||||
output, err := exec.Command("go", "mod", "download", "-json", module+"@"+version).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to download %s@%s: %s\n%s\n", module, version, err, output)
|
||||
}
|
||||
var j struct {
|
||||
Dir string
|
||||
}
|
||||
if err := json.Unmarshal(output, &j); err != nil {
|
||||
t.Fatalf("failed to parse 'go mod download': %s\n%s\n", err, output)
|
||||
}
|
||||
|
||||
return j.Dir
|
||||
}
|
||||
|
||||
func prepareBinaries(t *testing.T, pebbleDir string) string {
|
||||
t.Helper()
|
||||
|
||||
// We don't want to build in the module cache dir, which might not be
|
||||
// writable or to pollute the user's clone with binaries if pebbleLocalDir
|
||||
//is used.
|
||||
binDir := t.TempDir()
|
||||
|
||||
build := func(cmd string) {
|
||||
log.Printf("building %s", cmd)
|
||||
buildCmd := exec.Command(
|
||||
"go",
|
||||
"build", "-o", filepath.Join(binDir, cmd), "-mod", "mod", "./cmd/"+cmd)
|
||||
buildCmd.Dir = pebbleDir
|
||||
output, err := buildCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to build %s: %s\n%s\n", cmd, err, output)
|
||||
}
|
||||
}
|
||||
|
||||
build("pebble")
|
||||
build("pebble-challtestsrv")
|
||||
|
||||
return binDir
|
||||
}
|
||||
|
||||
func spawnServerProcess(t *testing.T, dir string, cmd string, args ...string) {
|
||||
t.Helper()
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
cmdInstance := exec.Command("./"+cmd, args...)
|
||||
cmdInstance.Dir = dir
|
||||
cmdInstance.Stdout = &stdout
|
||||
cmdInstance.Stderr = &stderr
|
||||
|
||||
if err := cmdInstance.Start(); err != nil {
|
||||
t.Fatalf("failed to start %s: %v", cmd, err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
cmdInstance.Process.Kill()
|
||||
cmdInstance.Wait()
|
||||
|
||||
if t.Failed() || testing.Verbose() {
|
||||
t.Logf("=== %s output ===", cmd)
|
||||
if stdout.Len() > 0 {
|
||||
t.Logf("stdout:\n%s", strings.TrimSpace(stdout.String()))
|
||||
}
|
||||
if stderr.Len() > 0 {
|
||||
t.Logf("stderr:\n%s", strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
+2
-2
@@ -272,7 +272,7 @@ func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
|
||||
case err != nil:
|
||||
// Skip and retry.
|
||||
case o.Status == StatusInvalid:
|
||||
return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
|
||||
return nil, &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
|
||||
case o.Status == StatusReady || o.Status == StatusValid:
|
||||
return o, nil
|
||||
}
|
||||
@@ -369,7 +369,7 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu
|
||||
}
|
||||
// The only acceptable status post finalize and WaitOrder is "valid".
|
||||
if o.Status != StatusValid {
|
||||
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
|
||||
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
|
||||
}
|
||||
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
|
||||
return crt, o.CertURL, err
|
||||
|
||||
+16
-3
@@ -885,11 +885,17 @@ func TestRFC_WaitOrderError(t *testing.T) {
|
||||
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Location", s.url("/orders/1"))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
s := StatusPending
|
||||
if count > 0 {
|
||||
s = StatusInvalid
|
||||
// https://www.rfc-editor.org/rfc/rfc8555#section-7.3.3
|
||||
errorData := `{
|
||||
"type": "urn:ietf:params:acme:error:userActionRequired",
|
||||
"detail": "Terms of service have changed",
|
||||
"instance": "https://example.com/acme/agreement/?token=W8Ih3PswD-8"
|
||||
}`
|
||||
fmt.Fprintf(w, `{"status": %q, "error": %s}`, StatusInvalid, errorData)
|
||||
} else {
|
||||
fmt.Fprintf(w, `{"status": %q}`, StatusPending)
|
||||
}
|
||||
fmt.Fprintf(w, `{"status": %q}`, s)
|
||||
count++
|
||||
})
|
||||
s.start()
|
||||
@@ -910,6 +916,13 @@ func TestRFC_WaitOrderError(t *testing.T) {
|
||||
if e.Status != StatusInvalid {
|
||||
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
|
||||
}
|
||||
if e.Problem == nil {
|
||||
t.Errorf("e.Problem = nil")
|
||||
}
|
||||
expectedProbType := "urn:ietf:params:acme:error:userActionRequired"
|
||||
if e.Problem.ProblemType != expectedProbType {
|
||||
t.Errorf("e.Problem.ProblemType = %q; want %q", e.Problem.ProblemType, expectedProbType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRFC_CreateOrderCert(t *testing.T) {
|
||||
|
||||
+21
-3
@@ -7,6 +7,7 @@ package acme
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -55,6 +56,10 @@ var (
|
||||
|
||||
// ErrNoAccount indicates that the Client's key has not been registered with the CA.
|
||||
ErrNoAccount = errors.New("acme: account does not exist")
|
||||
|
||||
// errPreAuthorizationNotSupported indicates that the server does not
|
||||
// support pre-authorization of identifiers.
|
||||
errPreAuthorizationNotSupported = errors.New("acme: pre-authorization is not supported")
|
||||
)
|
||||
|
||||
// A Subproblem describes an ACME subproblem as reported in an Error.
|
||||
@@ -149,13 +154,16 @@ func (a *AuthorizationError) Error() string {
|
||||
|
||||
// OrderError is returned from Client's order related methods.
|
||||
// It indicates the order is unusable and the clients should start over with
|
||||
// AuthorizeOrder.
|
||||
// AuthorizeOrder. A Problem description may be provided with details on
|
||||
// what caused the order to become unusable.
|
||||
//
|
||||
// The clients can still fetch the order object from CA using GetOrder
|
||||
// to inspect its state.
|
||||
type OrderError struct {
|
||||
OrderURL string
|
||||
Status string
|
||||
// Problem is the error that occurred while processing the order.
|
||||
Problem *Error
|
||||
}
|
||||
|
||||
func (oe *OrderError) Error() string {
|
||||
@@ -288,7 +296,7 @@ type Directory struct {
|
||||
// KeyChangeURL allows to perform account key rollover flow.
|
||||
KeyChangeURL string
|
||||
|
||||
// Term is a URI identifying the current terms of service.
|
||||
// Terms is a URI identifying the current terms of service.
|
||||
Terms string
|
||||
|
||||
// Website is an HTTP or HTTPS URL locating a website
|
||||
@@ -527,6 +535,16 @@ type Challenge struct {
|
||||
// when this challenge was used.
|
||||
// The type of a non-nil value is *Error.
|
||||
Error error
|
||||
|
||||
// Payload is the JSON-formatted payload that the client sends
|
||||
// to the server to indicate it is ready to respond to the challenge.
|
||||
// When unset, it defaults to an empty JSON object: {}.
|
||||
// For most challenges, the client must not set Payload,
|
||||
// see https://tools.ietf.org/html/rfc8555#section-7.5.1.
|
||||
// Payload is used only for newer challenges (such as "device-attest-01")
|
||||
// where the client must send additional data for the server to validate
|
||||
// the challenge.
|
||||
Payload json.RawMessage
|
||||
}
|
||||
|
||||
// wireChallenge is ACME JSON challenge representation.
|
||||
@@ -604,7 +622,7 @@ func (*certOptKey) privateCertOpt() {}
|
||||
//
|
||||
// In TLS ChallengeCert methods, the template is also used as parent,
|
||||
// resulting in a self-signed certificate.
|
||||
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
||||
// The DNSNames or IPAddresses fields of t are always overwritten for tls-alpn challenge certs.
|
||||
func WithTemplate(t *x509.Certificate) CertOption {
|
||||
return (*certOptTemplate)(t)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright 2024 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 main
|
||||
|
||||
import (
|
||||
. "github.com/mmcloughlin/avo/build"
|
||||
. "github.com/mmcloughlin/avo/operand"
|
||||
. "github.com/mmcloughlin/avo/reg"
|
||||
_ "golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
//go:generate go run . -out ../blamka_amd64.s -pkg argon2
|
||||
|
||||
func main() {
|
||||
Package("golang.org/x/crypto/argon2")
|
||||
ConstraintExpr("amd64,gc,!purego")
|
||||
|
||||
blamkaSSE4()
|
||||
mixBlocksSSE2()
|
||||
xorBlocksSSE2()
|
||||
Generate()
|
||||
}
|
||||
|
||||
func blamkaSSE4() {
|
||||
Implement("blamkaSSE4")
|
||||
Attributes(NOSPLIT)
|
||||
AllocLocal(0)
|
||||
|
||||
Load(Param("b"), RAX)
|
||||
|
||||
c40 := c40_DATA()
|
||||
c48 := c48_DATA()
|
||||
|
||||
MOVOU(c40, X10)
|
||||
MOVOU(c48, X11)
|
||||
|
||||
BLAMKA_ROUND_0(AX, 0, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 16, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 32, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 48, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 64, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 80, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 96, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 112, X8, X9, X10, X11)
|
||||
|
||||
BLAMKA_ROUND_1(AX, 0, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 2, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 4, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 6, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 8, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 10, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 12, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 14, X8, X9, X10, X11)
|
||||
RET()
|
||||
}
|
||||
|
||||
func mixBlocksSSE2() {
|
||||
Implement("mixBlocksSSE2")
|
||||
Attributes(NOSPLIT)
|
||||
AllocLocal(0)
|
||||
|
||||
Load(Param("out"), RDX)
|
||||
Load(Param("a"), RAX)
|
||||
Load(Param("b"), RBX)
|
||||
Load(Param("c"), RCX)
|
||||
MOVQ(U32(128), RDI)
|
||||
|
||||
Label("loop")
|
||||
MOVOU(Mem{Base: AX}.Offset(0), X0)
|
||||
MOVOU(Mem{Base: BX}.Offset(0), X1)
|
||||
MOVOU(Mem{Base: CX}.Offset(0), X2)
|
||||
PXOR(X1, X0)
|
||||
PXOR(X2, X0)
|
||||
MOVOU(X0, Mem{Base: DX}.Offset(0))
|
||||
ADDQ(Imm(16), RAX)
|
||||
ADDQ(Imm(16), RBX)
|
||||
ADDQ(Imm(16), RCX)
|
||||
ADDQ(Imm(16), RDX)
|
||||
SUBQ(Imm(2), RDI)
|
||||
JA(LabelRef("loop"))
|
||||
RET()
|
||||
}
|
||||
|
||||
func xorBlocksSSE2() {
|
||||
Implement("xorBlocksSSE2")
|
||||
Attributes(NOSPLIT)
|
||||
AllocLocal(0)
|
||||
|
||||
Load(Param("out"), RDX)
|
||||
Load(Param("a"), RAX)
|
||||
Load(Param("b"), RBX)
|
||||
Load(Param("c"), RCX)
|
||||
MOVQ(U32(128), RDI)
|
||||
|
||||
Label("loop")
|
||||
MOVOU(Mem{Base: AX}.Offset(0), X0)
|
||||
MOVOU(Mem{Base: BX}.Offset(0), X1)
|
||||
MOVOU(Mem{Base: CX}.Offset(0), X2)
|
||||
MOVOU(Mem{Base: DX}.Offset(0), X3)
|
||||
PXOR(X1, X0)
|
||||
PXOR(X2, X0)
|
||||
PXOR(X3, X0)
|
||||
MOVOU(X0, Mem{Base: DX}.Offset(0))
|
||||
ADDQ(Imm(16), RAX)
|
||||
ADDQ(Imm(16), RBX)
|
||||
ADDQ(Imm(16), RCX)
|
||||
ADDQ(Imm(16), RDX)
|
||||
SUBQ(Imm(2), RDI)
|
||||
JA(LabelRef("loop"))
|
||||
RET()
|
||||
}
|
||||
|
||||
func SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
|
||||
MOVO(v4, t1)
|
||||
MOVO(v5, v4)
|
||||
MOVO(t1, v5)
|
||||
MOVO(v6, t1)
|
||||
PUNPCKLQDQ(v6, t2)
|
||||
PUNPCKHQDQ(v7, v6)
|
||||
PUNPCKHQDQ(t2, v6)
|
||||
PUNPCKLQDQ(v7, t2)
|
||||
MOVO(t1, v7)
|
||||
MOVO(v2, t1)
|
||||
PUNPCKHQDQ(t2, v7)
|
||||
PUNPCKLQDQ(v3, t2)
|
||||
PUNPCKHQDQ(t2, v2)
|
||||
PUNPCKLQDQ(t1, t2)
|
||||
PUNPCKHQDQ(t2, v3)
|
||||
}
|
||||
|
||||
func SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
|
||||
MOVO(v4, t1)
|
||||
MOVO(v5, v4)
|
||||
MOVO(t1, v5)
|
||||
MOVO(v2, t1)
|
||||
PUNPCKLQDQ(v2, t2)
|
||||
PUNPCKHQDQ(v3, v2)
|
||||
PUNPCKHQDQ(t2, v2)
|
||||
PUNPCKLQDQ(v3, t2)
|
||||
MOVO(t1, v3)
|
||||
MOVO(v6, t1)
|
||||
PUNPCKHQDQ(t2, v3)
|
||||
PUNPCKLQDQ(v7, t2)
|
||||
PUNPCKHQDQ(t2, v6)
|
||||
PUNPCKLQDQ(t1, t2)
|
||||
PUNPCKHQDQ(t2, v7)
|
||||
}
|
||||
|
||||
func HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, c40, c48 VecPhysical) {
|
||||
MOVO(v0, t0)
|
||||
PMULULQ(v2, t0)
|
||||
PADDQ(v2, v0)
|
||||
PADDQ(t0, v0)
|
||||
PADDQ(t0, v0)
|
||||
PXOR(v0, v6)
|
||||
PSHUFD(Imm(0xB1), v6, v6)
|
||||
MOVO(v4, t0)
|
||||
PMULULQ(v6, t0)
|
||||
PADDQ(v6, v4)
|
||||
PADDQ(t0, v4)
|
||||
PADDQ(t0, v4)
|
||||
PXOR(v4, v2)
|
||||
PSHUFB(c40, v2)
|
||||
MOVO(v0, t0)
|
||||
PMULULQ(v2, t0)
|
||||
PADDQ(v2, v0)
|
||||
PADDQ(t0, v0)
|
||||
PADDQ(t0, v0)
|
||||
PXOR(v0, v6)
|
||||
PSHUFB(c48, v6)
|
||||
MOVO(v4, t0)
|
||||
PMULULQ(v6, t0)
|
||||
PADDQ(v6, v4)
|
||||
PADDQ(t0, v4)
|
||||
PADDQ(t0, v4)
|
||||
PXOR(v4, v2)
|
||||
MOVO(v2, t0)
|
||||
PADDQ(v2, t0)
|
||||
PSRLQ(Imm(63), v2)
|
||||
PXOR(t0, v2)
|
||||
MOVO(v1, t0)
|
||||
PMULULQ(v3, t0)
|
||||
PADDQ(v3, v1)
|
||||
PADDQ(t0, v1)
|
||||
PADDQ(t0, v1)
|
||||
PXOR(v1, v7)
|
||||
PSHUFD(Imm(0xB1), v7, v7)
|
||||
MOVO(v5, t0)
|
||||
PMULULQ(v7, t0)
|
||||
PADDQ(v7, v5)
|
||||
PADDQ(t0, v5)
|
||||
PADDQ(t0, v5)
|
||||
PXOR(v5, v3)
|
||||
PSHUFB(c40, v3)
|
||||
MOVO(v1, t0)
|
||||
PMULULQ(v3, t0)
|
||||
PADDQ(v3, v1)
|
||||
PADDQ(t0, v1)
|
||||
PADDQ(t0, v1)
|
||||
PXOR(v1, v7)
|
||||
PSHUFB(c48, v7)
|
||||
MOVO(v5, t0)
|
||||
PMULULQ(v7, t0)
|
||||
PADDQ(v7, v5)
|
||||
PADDQ(t0, v5)
|
||||
PADDQ(t0, v5)
|
||||
PXOR(v5, v3)
|
||||
MOVO(v3, t0)
|
||||
PADDQ(v3, t0)
|
||||
PSRLQ(Imm(63), v3)
|
||||
PXOR(t0, v3)
|
||||
}
|
||||
|
||||
func LOAD_MSG_0(block GPPhysical, off int) {
|
||||
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
|
||||
for i, r := range registers {
|
||||
MOVOU(Mem{Base: block}.Offset(8*(off+(i*2))), r)
|
||||
}
|
||||
}
|
||||
|
||||
func STORE_MSG_0(block GPPhysical, off int) {
|
||||
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
|
||||
for i, r := range registers {
|
||||
MOVOU(r, Mem{Base: block}.Offset(8*(off+(i*2))))
|
||||
}
|
||||
}
|
||||
|
||||
func LOAD_MSG_1(block GPPhysical, off int) {
|
||||
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
|
||||
for i, r := range registers {
|
||||
MOVOU(Mem{Base: block}.Offset(8*off+i*16*8), r)
|
||||
}
|
||||
}
|
||||
|
||||
func STORE_MSG_1(block GPPhysical, off int) {
|
||||
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
|
||||
for i, r := range registers {
|
||||
MOVOU(r, Mem{Base: block}.Offset(8*off+i*16*8))
|
||||
}
|
||||
}
|
||||
|
||||
func BLAMKA_ROUND_0(block GPPhysical, off int, t0, t1, c40, c48 VecPhysical) {
|
||||
LOAD_MSG_0(block, off)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1)
|
||||
STORE_MSG_0(block, off)
|
||||
}
|
||||
|
||||
func BLAMKA_ROUND_1(block GPPhysical, off int, t0, t1, c40, c48 VecPhysical) {
|
||||
LOAD_MSG_1(block, off)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1)
|
||||
STORE_MSG_1(block, off)
|
||||
}
|
||||
|
||||
// ##------------------DATA SECTION-------------------##
|
||||
|
||||
var c40_DATA_ptr, c48_DATA_ptr *Mem
|
||||
|
||||
func c40_DATA() Mem {
|
||||
if c40_DATA_ptr != nil {
|
||||
return *c40_DATA_ptr
|
||||
}
|
||||
|
||||
c40_DATA := GLOBL("·c40", NOPTR|RODATA)
|
||||
c40_DATA_ptr = &c40_DATA
|
||||
DATA(0x00, U64(0x0201000706050403))
|
||||
DATA(0x08, U64(0x0a09080f0e0d0c0b))
|
||||
return c40_DATA
|
||||
}
|
||||
func c48_DATA() Mem {
|
||||
if c48_DATA_ptr != nil {
|
||||
return *c48_DATA_ptr
|
||||
}
|
||||
|
||||
c48_DATA := GLOBL("·c48", NOPTR|RODATA)
|
||||
c48_DATA_ptr = &c48_DATA
|
||||
DATA(0x00, U64(0x0100070605040302))
|
||||
DATA(0x08, U64(0x09080f0e0d0c0b0a))
|
||||
return c48_DATA
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
module argon2/_asm
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
+2761
-213
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -50,7 +50,7 @@ func (ih InvalidHashPrefixError) Error() string {
|
||||
type InvalidCostError int
|
||||
|
||||
func (ic InvalidCostError) Error() string {
|
||||
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), MinCost, MaxCost)
|
||||
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed inclusive range %d..%d", int(ic), MinCost, MaxCost)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
module blake2b/_asm/AVX2
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
@@ -0,0 +1,361 @@
|
||||
// Copyright 2024 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 main
|
||||
|
||||
import (
|
||||
. "github.com/mmcloughlin/avo/build"
|
||||
. "github.com/mmcloughlin/avo/operand"
|
||||
. "github.com/mmcloughlin/avo/reg"
|
||||
_ "golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
//go:generate go run . -out ../../blake2b_amd64.s -pkg blake2b
|
||||
|
||||
const ThatPeskyUnicodeDot = "\u00b7"
|
||||
|
||||
var iv0_DATA_ptr, iv1_DATA_ptr, iv2_DATA_ptr, iv3_DATA_ptr, c40_DATA_ptr, c48_DATA_ptr *Mem
|
||||
|
||||
func main() {
|
||||
Package("golang.org/x/crypto/blake2b")
|
||||
ConstraintExpr("amd64,gc,!purego")
|
||||
hashBlocksSSE4()
|
||||
Generate()
|
||||
}
|
||||
|
||||
func SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
|
||||
MOVO(v4, t1)
|
||||
MOVO(v5, v4)
|
||||
MOVO(t1, v5)
|
||||
MOVO(v6, t1)
|
||||
PUNPCKLQDQ(v6, t2)
|
||||
PUNPCKHQDQ(v7, v6)
|
||||
PUNPCKHQDQ(t2, v6)
|
||||
PUNPCKLQDQ(v7, t2)
|
||||
MOVO(t1, v7)
|
||||
MOVO(v2, t1)
|
||||
PUNPCKHQDQ(t2, v7)
|
||||
PUNPCKLQDQ(v3, t2)
|
||||
PUNPCKHQDQ(t2, v2)
|
||||
PUNPCKLQDQ(t1, t2)
|
||||
PUNPCKHQDQ(t2, v3)
|
||||
}
|
||||
|
||||
func SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
|
||||
MOVO(v4, t1)
|
||||
MOVO(v5, v4)
|
||||
MOVO(t1, v5)
|
||||
MOVO(v2, t1)
|
||||
PUNPCKLQDQ(v2, t2)
|
||||
PUNPCKHQDQ(v3, v2)
|
||||
PUNPCKHQDQ(t2, v2)
|
||||
PUNPCKLQDQ(v3, t2)
|
||||
MOVO(t1, v3)
|
||||
MOVO(v6, t1)
|
||||
PUNPCKHQDQ(t2, v3)
|
||||
PUNPCKLQDQ(v7, t2)
|
||||
PUNPCKHQDQ(t2, v6)
|
||||
PUNPCKLQDQ(t1, t2)
|
||||
PUNPCKHQDQ(t2, v7)
|
||||
}
|
||||
|
||||
func HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7 VecPhysical, m0, m1, m2, m3 Op, t0, c40, c48 VecPhysical) {
|
||||
PADDQ(m0, v0)
|
||||
PADDQ(m1, v1)
|
||||
PADDQ(v2, v0)
|
||||
PADDQ(v3, v1)
|
||||
PXOR(v0, v6)
|
||||
PXOR(v1, v7)
|
||||
PSHUFD(Imm(0xB1), v6, v6)
|
||||
PSHUFD(Imm(0xB1), v7, v7)
|
||||
PADDQ(v6, v4)
|
||||
PADDQ(v7, v5)
|
||||
PXOR(v4, v2)
|
||||
PXOR(v5, v3)
|
||||
PSHUFB(c40, v2)
|
||||
PSHUFB(c40, v3)
|
||||
PADDQ(m2, v0)
|
||||
PADDQ(m3, v1)
|
||||
PADDQ(v2, v0)
|
||||
PADDQ(v3, v1)
|
||||
PXOR(v0, v6)
|
||||
PXOR(v1, v7)
|
||||
PSHUFB(c48, v6)
|
||||
PSHUFB(c48, v7)
|
||||
PADDQ(v6, v4)
|
||||
PADDQ(v7, v5)
|
||||
PXOR(v4, v2)
|
||||
PXOR(v5, v3)
|
||||
MOVOU(v2, t0)
|
||||
PADDQ(v2, t0)
|
||||
PSRLQ(Imm(63), v2)
|
||||
PXOR(t0, v2)
|
||||
MOVOU(v3, t0)
|
||||
PADDQ(v3, t0)
|
||||
PSRLQ(Imm(63), v3)
|
||||
PXOR(t0, v3)
|
||||
}
|
||||
|
||||
func LOAD_MSG(m0, m1, m2, m3 VecPhysical, src GPPhysical, i0, i1, i2, i3, i4, i5, i6, i7 int) {
|
||||
MOVQ(Mem{Base: src}.Offset(i0*8), m0)
|
||||
PINSRQ(Imm(1), Mem{Base: src}.Offset(i1*8), m0)
|
||||
MOVQ(Mem{Base: src}.Offset(i2*8), m1)
|
||||
PINSRQ(Imm(1), Mem{Base: src}.Offset(i3*8), m1)
|
||||
MOVQ(Mem{Base: src}.Offset(i4*8), m2)
|
||||
PINSRQ(Imm(1), Mem{Base: src}.Offset(i5*8), m2)
|
||||
MOVQ(Mem{Base: src}.Offset(i6*8), m3)
|
||||
PINSRQ(Imm(1), Mem{Base: src}.Offset(i7*8), m3)
|
||||
}
|
||||
|
||||
func hashBlocksSSE4() {
|
||||
Implement("hashBlocksSSE4")
|
||||
Attributes(4)
|
||||
AllocLocal(288) // frame size = 272 + 16 byte alignment
|
||||
|
||||
Load(Param("h"), RAX)
|
||||
Load(Param("c"), RBX)
|
||||
Load(Param("flag"), RCX)
|
||||
Load(Param("blocks").Base(), RSI)
|
||||
Load(Param("blocks").Len(), RDI)
|
||||
|
||||
MOVQ(RSP, R10)
|
||||
ADDQ(Imm(15), R10)
|
||||
ANDQ(I32(-16), R10)
|
||||
|
||||
iv3 := iv3_DATA()
|
||||
MOVOU(iv3, X0)
|
||||
MOVO(X0, Mem{Base: R10}.Offset(0))
|
||||
XORQ(RCX, Mem{Base: R10}.Offset(0)) // 0(R10) = ·iv3 ^ (CX || 0)
|
||||
|
||||
c40 := c40_DATA()
|
||||
c48 := c48_DATA()
|
||||
MOVOU(c40, X13)
|
||||
MOVOU(c48, X14)
|
||||
|
||||
MOVOU(Mem{Base: AX}.Offset(0), X12)
|
||||
MOVOU(Mem{Base: AX}.Offset(16), X15)
|
||||
|
||||
MOVQ(Mem{Base: BX}.Offset(0), R8)
|
||||
MOVQ(Mem{Base: BX}.Offset(8), R9)
|
||||
|
||||
Label("loop")
|
||||
ADDQ(Imm(128), R8)
|
||||
CMPQ(R8, Imm(128))
|
||||
JGE(LabelRef("noinc"))
|
||||
INCQ(R9)
|
||||
|
||||
Label("noinc")
|
||||
MOVQ(R8, X8)
|
||||
PINSRQ(Imm(1), R9, X8)
|
||||
|
||||
iv0 := iv0_DATA()
|
||||
iv1 := iv1_DATA()
|
||||
iv2 := iv2_DATA()
|
||||
|
||||
MOVO(X12, X0)
|
||||
MOVO(X15, X1)
|
||||
MOVOU(Mem{Base: AX}.Offset(32), X2)
|
||||
MOVOU(Mem{Base: AX}.Offset(48), X3)
|
||||
MOVOU(iv0, X4)
|
||||
MOVOU(iv1, X5)
|
||||
MOVOU(iv2, X6)
|
||||
|
||||
PXOR(X8, X6)
|
||||
MOVO(Mem{Base: R10}.Offset(0), X7)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7)
|
||||
MOVO(X8, Mem{Base: R10}.Offset(16))
|
||||
MOVO(X9, Mem{Base: R10}.Offset(32))
|
||||
MOVO(X10, Mem{Base: R10}.Offset(48))
|
||||
MOVO(X11, Mem{Base: R10}.Offset(64))
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15)
|
||||
MOVO(X8, Mem{Base: R10}.Offset(80))
|
||||
MOVO(X9, Mem{Base: R10}.Offset(96))
|
||||
MOVO(X10, Mem{Base: R10}.Offset(112))
|
||||
MOVO(X11, Mem{Base: R10}.Offset(128))
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6)
|
||||
MOVO(X8, Mem{Base: R10}.Offset(144))
|
||||
MOVO(X9, Mem{Base: R10}.Offset(160))
|
||||
MOVO(X10, Mem{Base: R10}.Offset(176))
|
||||
MOVO(X11, Mem{Base: R10}.Offset(192))
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3)
|
||||
MOVO(X8, Mem{Base: R10}.Offset(208))
|
||||
MOVO(X9, Mem{Base: R10}.Offset(224))
|
||||
MOVO(X10, Mem{Base: R10}.Offset(240))
|
||||
MOVO(X11, Mem{Base: R10}.Offset(256))
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 10, 3, 7, 9, 14, 6, 1, 4)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 2, 5, 4, 15, 6, 10, 0, 8)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 14, 11, 6, 3, 1, 12, 8, 13)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 4, 7, 15, 1, 13, 5, 14, 9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 0, 6, 9, 8, 7, 3, 2, 11)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 5, 15, 8, 2, 0, 4, 6, 10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 12, 13, 1, 10, 2, 7, 4, 5)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 15, 9, 3, 13, 11, 14, 12, 0)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(16), Mem{Base: R10}.Offset(32), Mem{Base: R10}.Offset(48), Mem{Base: R10}.Offset(64), X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(80), Mem{Base: R10}.Offset(96), Mem{Base: R10}.Offset(112), Mem{Base: R10}.Offset(128), X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(144), Mem{Base: R10}.Offset(160), Mem{Base: R10}.Offset(176), Mem{Base: R10}.Offset(192), X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(208), Mem{Base: R10}.Offset(224), Mem{Base: R10}.Offset(240), Mem{Base: R10}.Offset(256), X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
MOVOU(Mem{Base: AX}.Offset(32), X10)
|
||||
MOVOU(Mem{Base: AX}.Offset(48), X11)
|
||||
PXOR(X0, X12)
|
||||
PXOR(X1, X15)
|
||||
PXOR(X2, X10)
|
||||
PXOR(X3, X11)
|
||||
PXOR(X4, X12)
|
||||
PXOR(X5, X15)
|
||||
PXOR(X6, X10)
|
||||
PXOR(X7, X11)
|
||||
MOVOU(X10, Mem{Base: AX}.Offset(32))
|
||||
MOVOU(X11, Mem{Base: AX}.Offset(48))
|
||||
|
||||
LEAQ(Mem{Base: SI}.Offset(128), RSI)
|
||||
SUBQ(Imm(128), RDI)
|
||||
JNE(LabelRef("loop"))
|
||||
|
||||
MOVOU(X12, Mem{Base: AX}.Offset(0))
|
||||
MOVOU(X15, Mem{Base: AX}.Offset(16))
|
||||
|
||||
MOVQ(R8, Mem{Base: BX}.Offset(0))
|
||||
MOVQ(R9, Mem{Base: BX}.Offset(8))
|
||||
|
||||
RET()
|
||||
}
|
||||
|
||||
// #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DATA SECTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
|
||||
|
||||
func iv0_DATA() Mem {
|
||||
if iv0_DATA_ptr != nil {
|
||||
return *iv0_DATA_ptr
|
||||
}
|
||||
|
||||
iv0 := GLOBL(ThatPeskyUnicodeDot+"iv0", NOPTR|RODATA)
|
||||
iv0_DATA_ptr = &iv0
|
||||
DATA(0x00, U64(0x6a09e667f3bcc908))
|
||||
DATA(0x08, U64(0xbb67ae8584caa73b))
|
||||
return iv0
|
||||
}
|
||||
|
||||
func iv1_DATA() Mem {
|
||||
if iv1_DATA_ptr != nil {
|
||||
return *iv1_DATA_ptr
|
||||
}
|
||||
|
||||
iv1 := GLOBL(ThatPeskyUnicodeDot+"iv1", NOPTR|RODATA)
|
||||
iv1_DATA_ptr = &iv1
|
||||
DATA(0x00, U64(0x3c6ef372fe94f82b))
|
||||
DATA(0x08, U64(0xa54ff53a5f1d36f1))
|
||||
return iv1
|
||||
}
|
||||
|
||||
func iv2_DATA() Mem {
|
||||
if iv2_DATA_ptr != nil {
|
||||
return *iv2_DATA_ptr
|
||||
}
|
||||
|
||||
iv2 := GLOBL(ThatPeskyUnicodeDot+"iv2", NOPTR|RODATA)
|
||||
iv2_DATA_ptr = &iv2
|
||||
DATA(0x00, U64(0x510e527fade682d1))
|
||||
DATA(0x08, U64(0x9b05688c2b3e6c1f))
|
||||
return iv2
|
||||
}
|
||||
|
||||
func iv3_DATA() Mem {
|
||||
if iv3_DATA_ptr != nil {
|
||||
return *iv3_DATA_ptr
|
||||
}
|
||||
|
||||
iv3 := GLOBL(ThatPeskyUnicodeDot+"iv3", NOPTR|RODATA)
|
||||
iv3_DATA_ptr = &iv3
|
||||
DATA(0x00, U64(0x1f83d9abfb41bd6b))
|
||||
DATA(0x08, U64(0x5be0cd19137e2179))
|
||||
return iv3
|
||||
}
|
||||
|
||||
func c40_DATA() Mem {
|
||||
if c40_DATA_ptr != nil {
|
||||
return *c40_DATA_ptr
|
||||
}
|
||||
|
||||
c40 := GLOBL(ThatPeskyUnicodeDot+"c40", NOPTR|RODATA)
|
||||
c40_DATA_ptr = &c40
|
||||
DATA(0x00, U64(0x0201000706050403))
|
||||
DATA(0x08, U64(0x0a09080f0e0d0c0b))
|
||||
return c40
|
||||
}
|
||||
|
||||
func c48_DATA() Mem {
|
||||
if c48_DATA_ptr != nil {
|
||||
return *c48_DATA_ptr
|
||||
}
|
||||
|
||||
c48 := GLOBL(ThatPeskyUnicodeDot+"c48", NOPTR|RODATA)
|
||||
c48_DATA_ptr = &c48
|
||||
DATA(0x00, U64(0x0100070605040302))
|
||||
DATA(0x08, U64(0x09080f0e0d0c0b0a))
|
||||
return c48
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
module blake2b/_asm
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
+4492
-677
File diff suppressed because it is too large
Load Diff
+1427
-264
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
// XOF defines the interface to hash functions that
|
||||
// support arbitrary-length output.
|
||||
//
|
||||
// New callers should prefer the standard library [hash.XOF].
|
||||
type XOF interface {
|
||||
// Write absorbs more data into the hash's state. It panics if called
|
||||
// after Read.
|
||||
@@ -47,6 +49,8 @@ const maxOutputLength = (1 << 32) * 64
|
||||
//
|
||||
// A non-nil key turns the hash into a MAC. The key must between
|
||||
// zero and 32 bytes long.
|
||||
//
|
||||
// The result can be safely interface-upgraded to [hash.XOF].
|
||||
func NewXOF(size uint32, key []byte) (XOF, error) {
|
||||
if len(key) > Size {
|
||||
return nil, errKeySize
|
||||
@@ -93,6 +97,10 @@ func (x *xof) Clone() XOF {
|
||||
return &clone
|
||||
}
|
||||
|
||||
func (x *xof) BlockSize() int {
|
||||
return x.d.BlockSize()
|
||||
}
|
||||
|
||||
func (x *xof) Reset() {
|
||||
x.cfg[0] = byte(Size)
|
||||
binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
//go:build go1.25
|
||||
|
||||
package blake2b
|
||||
|
||||
import "hash"
|
||||
|
||||
var _ hash.XOF = (*xof)(nil)
|
||||
@@ -0,0 +1,525 @@
|
||||
// Copyright 2024 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 main
|
||||
|
||||
import (
|
||||
. "github.com/mmcloughlin/avo/build"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
. "github.com/mmcloughlin/avo/operand"
|
||||
. "github.com/mmcloughlin/avo/reg"
|
||||
_ "golang.org/x/crypto/blake2s"
|
||||
)
|
||||
|
||||
//go:generate go run . -out ../blake2s_amd64.s -pkg blake2s
|
||||
|
||||
func main() {
|
||||
Package("golang.org/x/crypto/blake2s")
|
||||
ConstraintExpr("amd64,gc,!purego")
|
||||
hashBlocksSSE2()
|
||||
hashBlocksSSSE3()
|
||||
hashBlocksSSE4()
|
||||
Generate()
|
||||
}
|
||||
|
||||
func ROTL_SSE2(n uint64, t, v VecPhysical) {
|
||||
MOVO(v, t)
|
||||
PSLLL(Imm(n), t)
|
||||
PSRLL(Imm(32-n), v)
|
||||
PXOR(t, v)
|
||||
}
|
||||
|
||||
func ROTL_SSSE3(c, v VecPhysical) {
|
||||
PSHUFB(c, v)
|
||||
}
|
||||
|
||||
func ROUND_SSE2(v0, v1, v2, v3 VecPhysical, m0, m1, m2, m3 Mem, t VecPhysical) {
|
||||
PADDL(m0, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSE2(16, t, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(20, t, v1)
|
||||
PADDL(m1, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSE2(24, t, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(25, t, v1)
|
||||
PSHUFL(Imm(0x39), v1, v1)
|
||||
PSHUFL(Imm(0x4E), v2, v2)
|
||||
PSHUFL(Imm(0x93), v3, v3)
|
||||
PADDL(m2, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSE2(16, t, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(20, t, v1)
|
||||
PADDL(m3, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSE2(24, t, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(25, t, v1)
|
||||
PSHUFL(Imm(0x39), v3, v3)
|
||||
PSHUFL(Imm(0x4E), v2, v2)
|
||||
PSHUFL(Imm(0x93), v1, v1)
|
||||
}
|
||||
|
||||
func ROUND_SSSE3(v0, v1, v2, v3 VecPhysical, m0, m1, m2, m3 Op, t, c16, c8 VecPhysical) {
|
||||
PADDL(m0, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSSE3(c16, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(20, t, v1)
|
||||
PADDL(m1, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSSE3(c8, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(25, t, v1)
|
||||
PSHUFL(Imm(0x39), v1, v1)
|
||||
PSHUFL(Imm(0x4E), v2, v2)
|
||||
PSHUFL(Imm(0x93), v3, v3)
|
||||
PADDL(m2, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSSE3(c16, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(20, t, v1)
|
||||
PADDL(m3, v0)
|
||||
PADDL(v1, v0)
|
||||
PXOR(v0, v3)
|
||||
ROTL_SSSE3(c8, v3)
|
||||
PADDL(v3, v2)
|
||||
PXOR(v2, v1)
|
||||
ROTL_SSE2(25, t, v1)
|
||||
PSHUFL(Imm(0x39), v3, v3)
|
||||
PSHUFL(Imm(0x4E), v2, v2)
|
||||
PSHUFL(Imm(0x93), v1, v1)
|
||||
}
|
||||
|
||||
func LOAD_MSG_SSE4(m0, m1, m2, m3 VecPhysical, src GPPhysical, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 int) {
|
||||
// Hack to get Avo to emit a MOVL instruction with a VecPhysical as the destination
|
||||
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i0 * 4), m0}})
|
||||
PINSRD(Imm(1), Mem{Base: src}.Offset(i1*4), m0)
|
||||
PINSRD(Imm(2), Mem{Base: src}.Offset(i2*4), m0)
|
||||
PINSRD(Imm(3), Mem{Base: src}.Offset(i3*4), m0)
|
||||
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i4 * 4), m1}})
|
||||
PINSRD(Imm(1), Mem{Base: src}.Offset(i5*4), m1)
|
||||
PINSRD(Imm(2), Mem{Base: src}.Offset(i6*4), m1)
|
||||
PINSRD(Imm(3), Mem{Base: src}.Offset(i7*4), m1)
|
||||
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i8 * 4), m2}})
|
||||
PINSRD(Imm(1), Mem{Base: src}.Offset(i9*4), m2)
|
||||
PINSRD(Imm(2), Mem{Base: src}.Offset(i10*4), m2)
|
||||
PINSRD(Imm(3), Mem{Base: src}.Offset(i11*4), m2)
|
||||
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i12 * 4), m3}})
|
||||
PINSRD(Imm(1), Mem{Base: src}.Offset(i13*4), m3)
|
||||
PINSRD(Imm(2), Mem{Base: src}.Offset(i14*4), m3)
|
||||
PINSRD(Imm(3), Mem{Base: src}.Offset(i15*4), m3)
|
||||
}
|
||||
|
||||
func PRECOMPUTE_MSG(dst GPPhysical, off int, src, R8, R9, R10, R11, R12, R13, R14, R15 GPPhysical) {
|
||||
MOVQ(Mem{Base: src}.Offset(0*4), R8)
|
||||
MOVQ(Mem{Base: src}.Offset(2*4), R9)
|
||||
MOVQ(Mem{Base: src}.Offset(4*4), R10)
|
||||
MOVQ(Mem{Base: src}.Offset(6*4), R11)
|
||||
MOVQ(Mem{Base: src}.Offset(8*4), R12)
|
||||
MOVQ(Mem{Base: src}.Offset(10*4), R13)
|
||||
MOVQ(Mem{Base: src}.Offset(12*4), R14)
|
||||
MOVQ(Mem{Base: src}.Offset(14*4), R15)
|
||||
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(0*4+off+0))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(9*4+off+64))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(5*4+off+128))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(14*4+off+192))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(4*4+off+256))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(2*4+off+320))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(8*4+off+384))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(12*4+off+448))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(3*4+off+512))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(15*4+off+576))
|
||||
SHRQ(Imm(32), R8)
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(4*4+off+0))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(8*4+off+64))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(14*4+off+128))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(5*4+off+192))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(12*4+off+256))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(11*4+off+320))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(1*4+off+384))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(6*4+off+448))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(10*4+off+512))
|
||||
MOVL(R8L, Mem{Base: dst}.Offset(3*4+off+576))
|
||||
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(1*4+off+0))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(13*4+off+64))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(6*4+off+128))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(8*4+off+192))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(2*4+off+256))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(0*4+off+320))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(14*4+off+384))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(11*4+off+448))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(12*4+off+512))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(4*4+off+576))
|
||||
SHRQ(Imm(32), R9)
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(5*4+off+0))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(15*4+off+64))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(9*4+off+128))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(1*4+off+192))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(11*4+off+256))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(7*4+off+320))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(13*4+off+384))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(3*4+off+448))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(6*4+off+512))
|
||||
MOVL(R9L, Mem{Base: dst}.Offset(10*4+off+576))
|
||||
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(2*4+off+0))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(1*4+off+64))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(15*4+off+128))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(10*4+off+192))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(6*4+off+256))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(8*4+off+320))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(3*4+off+384))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(13*4+off+448))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(14*4+off+512))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(5*4+off+576))
|
||||
SHRQ(Imm(32), R10)
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(6*4+off+0))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(11*4+off+64))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(2*4+off+128))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(9*4+off+192))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(1*4+off+256))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(13*4+off+320))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(4*4+off+384))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(8*4+off+448))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(15*4+off+512))
|
||||
MOVL(R10L, Mem{Base: dst}.Offset(7*4+off+576))
|
||||
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(3*4+off+0))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(7*4+off+64))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(13*4+off+128))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(12*4+off+192))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(10*4+off+256))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(1*4+off+320))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(9*4+off+384))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(14*4+off+448))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(0*4+off+512))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(6*4+off+576))
|
||||
SHRQ(Imm(32), R11)
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(7*4+off+0))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(14*4+off+64))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(10*4+off+128))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(0*4+off+192))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(5*4+off+256))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(9*4+off+320))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(12*4+off+384))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(1*4+off+448))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(13*4+off+512))
|
||||
MOVL(R11L, Mem{Base: dst}.Offset(2*4+off+576))
|
||||
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(8*4+off+0))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(5*4+off+64))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(4*4+off+128))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(15*4+off+192))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(14*4+off+256))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(3*4+off+320))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(11*4+off+384))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(10*4+off+448))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(7*4+off+512))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(1*4+off+576))
|
||||
SHRQ(Imm(32), R12)
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(12*4+off+0))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(2*4+off+64))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(11*4+off+128))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(4*4+off+192))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(0*4+off+256))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(15*4+off+320))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(10*4+off+384))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(7*4+off+448))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(5*4+off+512))
|
||||
MOVL(R12L, Mem{Base: dst}.Offset(9*4+off+576))
|
||||
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(9*4+off+0))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(4*4+off+64))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(8*4+off+128))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(13*4+off+192))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(3*4+off+256))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(5*4+off+320))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(7*4+off+384))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(15*4+off+448))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(11*4+off+512))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(0*4+off+576))
|
||||
SHRQ(Imm(32), R13)
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(13*4+off+0))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(10*4+off+64))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(0*4+off+128))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(3*4+off+192))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(9*4+off+256))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(6*4+off+320))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(15*4+off+384))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(4*4+off+448))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(2*4+off+512))
|
||||
MOVL(R13L, Mem{Base: dst}.Offset(12*4+off+576))
|
||||
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(10*4+off+0))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(12*4+off+64))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(1*4+off+128))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(6*4+off+192))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(13*4+off+256))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(4*4+off+320))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(0*4+off+384))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(2*4+off+448))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(8*4+off+512))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(14*4+off+576))
|
||||
SHRQ(Imm(32), R14)
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(14*4+off+0))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(3*4+off+64))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(7*4+off+128))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(2*4+off+192))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(15*4+off+256))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(12*4+off+320))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(6*4+off+384))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(0*4+off+448))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(9*4+off+512))
|
||||
MOVL(R14L, Mem{Base: dst}.Offset(11*4+off+576))
|
||||
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(11*4+off+0))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(0*4+off+64))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(12*4+off+128))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(7*4+off+192))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(8*4+off+256))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(14*4+off+320))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(2*4+off+384))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(5*4+off+448))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(1*4+off+512))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(13*4+off+576))
|
||||
SHRQ(Imm(32), R15)
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(15*4+off+0))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(6*4+off+64))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(3*4+off+128))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(11*4+off+192))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(7*4+off+256))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(10*4+off+320))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(5*4+off+384))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(9*4+off+448))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(4*4+off+512))
|
||||
MOVL(R15L, Mem{Base: dst}.Offset(8*4+off+576))
|
||||
}
|
||||
|
||||
func BLAKE2s_SSE2() {
|
||||
PRECOMPUTE_MSG(BP, 16, SI, R8, R9, R10, R11, R12, R13, R14, R15)
|
||||
for i := 0; i < 10; i++ {
|
||||
ROUND_SSE2(X4, X5, X6, X7, Mem{Base: BP}.Offset(16+64*i), Mem{Base: BP}.Offset(32+64*i), Mem{Base: BP}.Offset(48+64*i), Mem{Base: BP}.Offset(64+64*i), X8)
|
||||
}
|
||||
}
|
||||
|
||||
func BLAKE2s_SSSE3() {
|
||||
PRECOMPUTE_MSG(BP, 16, SI, R8, R9, R10, R11, R12, R13, R14, R15)
|
||||
for i := 0; i < 10; i++ {
|
||||
ROUND_SSSE3(X4, X5, X6, X7, Mem{Base: BP}.Offset(16+64*i), Mem{Base: BP}.Offset(32+64*i), Mem{Base: BP}.Offset(48+64*i), Mem{Base: BP}.Offset(64+64*i), X8, X13, X14)
|
||||
}
|
||||
}
|
||||
|
||||
func BLAKE2s_SSE4() {
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0)
|
||||
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
|
||||
}
|
||||
|
||||
func HASH_BLOCKS(h, c, flag, blocks_base, blocks_len Mem, BLAKE2s_FUNC func()) {
|
||||
MOVQ(h, RAX)
|
||||
MOVQ(c, RBX)
|
||||
MOVL(flag, ECX)
|
||||
MOVQ(blocks_base, RSI)
|
||||
MOVQ(blocks_len, RDX)
|
||||
|
||||
MOVQ(RSP, RBP)
|
||||
ADDQ(Imm(15), RBP)
|
||||
ANDQ(I32(^15), RBP)
|
||||
|
||||
MOVQ(Mem{Base: BX}.Offset(0), R9)
|
||||
MOVQ(R9, Mem{Base: BP}.Offset(0))
|
||||
MOVQ(RCX, Mem{Base: BP}.Offset(8))
|
||||
|
||||
MOVOU(Mem{Base: AX}.Offset(0), X0)
|
||||
MOVOU(Mem{Base: AX}.Offset(16), X1)
|
||||
|
||||
iv0 := iv0_DATA()
|
||||
iv1 := iv1_DATA()
|
||||
MOVOU(iv0, X2)
|
||||
MOVOU(iv1, X3)
|
||||
|
||||
counter := counter_DATA()
|
||||
rol16 := rol16_DATA()
|
||||
rol8 := rol8_DATA()
|
||||
MOVOU(counter, X12)
|
||||
MOVOU(rol16, X13)
|
||||
MOVOU(rol8, X14)
|
||||
MOVO(Mem{Base: BP}.Offset(0), X15)
|
||||
|
||||
Label("loop")
|
||||
MOVO(X0, X4)
|
||||
MOVO(X1, X5)
|
||||
MOVO(X2, X6)
|
||||
MOVO(X3, X7)
|
||||
|
||||
PADDQ(X12, X15)
|
||||
PXOR(X15, X7)
|
||||
|
||||
BLAKE2s_FUNC()
|
||||
|
||||
PXOR(X4, X0)
|
||||
PXOR(X5, X1)
|
||||
PXOR(X6, X0)
|
||||
PXOR(X7, X1)
|
||||
|
||||
LEAQ(Mem{Base: SI}.Offset(64), RSI)
|
||||
SUBQ(Imm(64), RDX)
|
||||
JNE(LabelRef("loop"))
|
||||
|
||||
MOVO(X15, Mem{Base: BP}.Offset(0))
|
||||
MOVQ(Mem{Base: BP}.Offset(0), R9)
|
||||
MOVQ(R9, Mem{Base: BX}.Offset(0))
|
||||
|
||||
MOVOU(X0, Mem{Base: AX}.Offset(0))
|
||||
MOVOU(X1, Mem{Base: AX}.Offset(16))
|
||||
}
|
||||
|
||||
func hashBlocksSSE2() {
|
||||
Implement("hashBlocksSSE2")
|
||||
Attributes(0)
|
||||
AllocLocal(672) // frame = 656 + 16 byte alignment
|
||||
|
||||
h := NewParamAddr("h", 0)
|
||||
c := NewParamAddr("c", 8)
|
||||
flag := NewParamAddr("flag", 16)
|
||||
blocks_base := NewParamAddr("blocks_base", 24)
|
||||
blocks_len := NewParamAddr("blocks_len", 32)
|
||||
|
||||
HASH_BLOCKS(h, c, flag, blocks_base, blocks_len, BLAKE2s_SSE2)
|
||||
RET()
|
||||
}
|
||||
|
||||
func hashBlocksSSSE3() {
|
||||
Implement("hashBlocksSSSE3")
|
||||
Attributes(0)
|
||||
AllocLocal(672) // frame = 656 + 16 byte alignment
|
||||
|
||||
h := NewParamAddr("h", 0)
|
||||
c := NewParamAddr("c", 8)
|
||||
flag := NewParamAddr("flag", 16)
|
||||
blocks_base := NewParamAddr("blocks_base", 24)
|
||||
blocks_len := NewParamAddr("blocks_len", 32)
|
||||
|
||||
HASH_BLOCKS(h, c, flag, blocks_base, blocks_len, BLAKE2s_SSSE3)
|
||||
RET()
|
||||
}
|
||||
|
||||
func hashBlocksSSE4() {
|
||||
Implement("hashBlocksSSE4")
|
||||
Attributes(0)
|
||||
AllocLocal(32) // frame = 16 + 16 byte alignment
|
||||
|
||||
h := NewParamAddr("h", 0)
|
||||
c := NewParamAddr("c", 8)
|
||||
flag := NewParamAddr("flag", 16)
|
||||
blocks_base := NewParamAddr("blocks_base", 24)
|
||||
blocks_len := NewParamAddr("blocks_len", 32)
|
||||
|
||||
HASH_BLOCKS(h, c, flag, blocks_base, blocks_len, BLAKE2s_SSE4)
|
||||
RET()
|
||||
}
|
||||
|
||||
// ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DATA SECTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
|
||||
|
||||
var iv0_DATA_ptr, iv1_DATA_ptr, rol16_DATA_ptr, rol8_DATA_ptr, counter_DATA_ptr *Mem
|
||||
|
||||
func iv0_DATA() Mem {
|
||||
if iv0_DATA_ptr != nil {
|
||||
return *iv0_DATA_ptr
|
||||
}
|
||||
|
||||
iv0_DATA := GLOBL("iv0", NOPTR|RODATA)
|
||||
iv0_DATA_ptr = &iv0_DATA
|
||||
DATA(0x00, U32(0x6a09e667))
|
||||
DATA(0x04, U32(0xbb67ae85))
|
||||
DATA(0x08, U32(0x3c6ef372))
|
||||
DATA(0x0c, U32(0xa54ff53a))
|
||||
return iv0_DATA
|
||||
}
|
||||
|
||||
func iv1_DATA() Mem {
|
||||
if iv1_DATA_ptr != nil {
|
||||
return *iv1_DATA_ptr
|
||||
}
|
||||
|
||||
iv1_DATA := GLOBL("iv1", NOPTR|RODATA)
|
||||
iv1_DATA_ptr = &iv1_DATA
|
||||
DATA(0x00, U32(0x510e527f))
|
||||
DATA(0x04, U32(0x9b05688c))
|
||||
DATA(0x08, U32(0x1f83d9ab))
|
||||
DATA(0x0c, U32(0x5be0cd19))
|
||||
return iv1_DATA
|
||||
}
|
||||
|
||||
func rol16_DATA() Mem {
|
||||
if rol16_DATA_ptr != nil {
|
||||
return *rol16_DATA_ptr
|
||||
}
|
||||
|
||||
rol16_DATA := GLOBL("rol16", NOPTR|RODATA)
|
||||
rol16_DATA_ptr = &rol16_DATA
|
||||
DATA(0x00, U64(0x0504070601000302))
|
||||
DATA(0x08, U64(0x0D0C0F0E09080B0A))
|
||||
return rol16_DATA
|
||||
}
|
||||
|
||||
func rol8_DATA() Mem {
|
||||
if rol8_DATA_ptr != nil {
|
||||
return *rol8_DATA_ptr
|
||||
}
|
||||
|
||||
rol8_DATA := GLOBL("rol8", NOPTR|RODATA)
|
||||
rol8_DATA_ptr = &rol8_DATA
|
||||
DATA(0x00, U64(0x0407060500030201))
|
||||
DATA(0x08, U64(0x0C0F0E0D080B0A09))
|
||||
return rol8_DATA
|
||||
}
|
||||
|
||||
func counter_DATA() Mem {
|
||||
if counter_DATA_ptr != nil {
|
||||
return *counter_DATA_ptr
|
||||
}
|
||||
|
||||
counter_DATA := GLOBL("counter", NOPTR|RODATA)
|
||||
counter_DATA_ptr = &counter_DATA
|
||||
DATA(0x00, U64(0x0000000000000040))
|
||||
DATA(0x08, U64(0x0000000000000000))
|
||||
return counter_DATA
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
module blake2s/_asm
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
+2160
-419
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -5,7 +5,7 @@
|
||||
package bn256
|
||||
|
||||
// For details of the algorithms used, see "Multiplication and Squaring on
|
||||
// Pairing-Friendly Fields, Devegili et al.
|
||||
// Pairing-Friendly Fields", Devegili et al.
|
||||
// http://eprint.iacr.org/2006/471.pdf.
|
||||
|
||||
import (
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
package bn256
|
||||
|
||||
// For details of the algorithms used, see "Multiplication and Squaring on
|
||||
// Pairing-Friendly Fields, Devegili et al.
|
||||
// Pairing-Friendly Fields", Devegili et al.
|
||||
// http://eprint.iacr.org/2006/471.pdf.
|
||||
|
||||
import (
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
package bn256
|
||||
|
||||
// For details of the algorithms used, see "Multiplication and Squaring on
|
||||
// Pairing-Friendly Fields, Devegili et al.
|
||||
// Pairing-Friendly Fields", Devegili et al.
|
||||
// http://eprint.iacr.org/2006/471.pdf.
|
||||
|
||||
import (
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego
|
||||
//go:build (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
package chacha20
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// The differences in this and the original implementation are
|
||||
// due to the calling conventions and initialization of constants.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
@@ -36,32 +36,68 @@
|
||||
// for VPERMXOR
|
||||
#define MASK R18
|
||||
|
||||
DATA consts<>+0x00(SB)/8, $0x3320646e61707865
|
||||
DATA consts<>+0x08(SB)/8, $0x6b20657479622d32
|
||||
DATA consts<>+0x10(SB)/8, $0x0000000000000001
|
||||
DATA consts<>+0x18(SB)/8, $0x0000000000000000
|
||||
DATA consts<>+0x20(SB)/8, $0x0000000000000004
|
||||
DATA consts<>+0x28(SB)/8, $0x0000000000000000
|
||||
DATA consts<>+0x30(SB)/8, $0x0a0b08090e0f0c0d
|
||||
DATA consts<>+0x38(SB)/8, $0x0203000106070405
|
||||
DATA consts<>+0x40(SB)/8, $0x090a0b080d0e0f0c
|
||||
DATA consts<>+0x48(SB)/8, $0x0102030005060704
|
||||
DATA consts<>+0x50(SB)/8, $0x6170786561707865
|
||||
DATA consts<>+0x58(SB)/8, $0x6170786561707865
|
||||
DATA consts<>+0x60(SB)/8, $0x3320646e3320646e
|
||||
DATA consts<>+0x68(SB)/8, $0x3320646e3320646e
|
||||
DATA consts<>+0x70(SB)/8, $0x79622d3279622d32
|
||||
DATA consts<>+0x78(SB)/8, $0x79622d3279622d32
|
||||
DATA consts<>+0x80(SB)/8, $0x6b2065746b206574
|
||||
DATA consts<>+0x88(SB)/8, $0x6b2065746b206574
|
||||
DATA consts<>+0x90(SB)/8, $0x0000000100000000
|
||||
DATA consts<>+0x98(SB)/8, $0x0000000300000002
|
||||
DATA consts<>+0xa0(SB)/8, $0x5566774411223300
|
||||
DATA consts<>+0xa8(SB)/8, $0xddeeffcc99aabb88
|
||||
DATA consts<>+0xb0(SB)/8, $0x6677445522330011
|
||||
DATA consts<>+0xb8(SB)/8, $0xeeffccddaabb8899
|
||||
DATA consts<>+0x00(SB)/4, $0x61707865
|
||||
DATA consts<>+0x04(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x08(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x0c(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x10(SB)/4, $0x00000001
|
||||
DATA consts<>+0x14(SB)/4, $0x00000000
|
||||
DATA consts<>+0x18(SB)/4, $0x00000000
|
||||
DATA consts<>+0x1c(SB)/4, $0x00000000
|
||||
DATA consts<>+0x20(SB)/4, $0x00000004
|
||||
DATA consts<>+0x24(SB)/4, $0x00000000
|
||||
DATA consts<>+0x28(SB)/4, $0x00000000
|
||||
DATA consts<>+0x2c(SB)/4, $0x00000000
|
||||
DATA consts<>+0x30(SB)/4, $0x0e0f0c0d
|
||||
DATA consts<>+0x34(SB)/4, $0x0a0b0809
|
||||
DATA consts<>+0x38(SB)/4, $0x06070405
|
||||
DATA consts<>+0x3c(SB)/4, $0x02030001
|
||||
DATA consts<>+0x40(SB)/4, $0x0d0e0f0c
|
||||
DATA consts<>+0x44(SB)/4, $0x090a0b08
|
||||
DATA consts<>+0x48(SB)/4, $0x05060704
|
||||
DATA consts<>+0x4c(SB)/4, $0x01020300
|
||||
DATA consts<>+0x50(SB)/4, $0x61707865
|
||||
DATA consts<>+0x54(SB)/4, $0x61707865
|
||||
DATA consts<>+0x58(SB)/4, $0x61707865
|
||||
DATA consts<>+0x5c(SB)/4, $0x61707865
|
||||
DATA consts<>+0x60(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x64(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x68(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x6c(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x70(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x74(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x78(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x7c(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x80(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x84(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x88(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x8c(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x90(SB)/4, $0x00000000
|
||||
DATA consts<>+0x94(SB)/4, $0x00000001
|
||||
DATA consts<>+0x98(SB)/4, $0x00000002
|
||||
DATA consts<>+0x9c(SB)/4, $0x00000003
|
||||
DATA consts<>+0xa0(SB)/4, $0x11223300
|
||||
DATA consts<>+0xa4(SB)/4, $0x55667744
|
||||
DATA consts<>+0xa8(SB)/4, $0x99aabb88
|
||||
DATA consts<>+0xac(SB)/4, $0xddeeffcc
|
||||
DATA consts<>+0xb0(SB)/4, $0x22330011
|
||||
DATA consts<>+0xb4(SB)/4, $0x66774455
|
||||
DATA consts<>+0xb8(SB)/4, $0xaabb8899
|
||||
DATA consts<>+0xbc(SB)/4, $0xeeffccdd
|
||||
GLOBL consts<>(SB), RODATA, $0xc0
|
||||
|
||||
#ifdef GOARCH_ppc64
|
||||
#define BE_XXBRW_INIT() \
|
||||
LVSL (R0)(R0), V24 \
|
||||
VSPLTISB $3, V25 \
|
||||
VXOR V24, V25, V24 \
|
||||
|
||||
#define BE_XXBRW(vr) VPERM vr, vr, V24, vr
|
||||
#else
|
||||
#define BE_XXBRW_INIT()
|
||||
#define BE_XXBRW(vr)
|
||||
#endif
|
||||
|
||||
//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||
TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||
MOVD out+0(FP), OUT
|
||||
@@ -94,6 +130,8 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||
// Clear V27
|
||||
VXOR V27, V27, V27
|
||||
|
||||
BE_XXBRW_INIT()
|
||||
|
||||
// V28
|
||||
LXVW4X (CONSTBASE)(R11), VS60
|
||||
|
||||
@@ -299,6 +337,11 @@ loop_vsx:
|
||||
VADDUWM V8, V18, V8
|
||||
VADDUWM V12, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -327,6 +370,11 @@ loop_vsx:
|
||||
VADDUWM V9, V18, V8
|
||||
VADDUWM V13, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -334,8 +382,8 @@ loop_vsx:
|
||||
LXVW4X (INP)(R8), VS60
|
||||
LXVW4X (INP)(R9), VS61
|
||||
LXVW4X (INP)(R10), VS62
|
||||
VXOR V27, V0, V27
|
||||
|
||||
VXOR V27, V0, V27
|
||||
VXOR V28, V4, V28
|
||||
VXOR V29, V8, V29
|
||||
VXOR V30, V12, V30
|
||||
@@ -354,6 +402,11 @@ loop_vsx:
|
||||
VADDUWM V10, V18, V8
|
||||
VADDUWM V14, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -381,6 +434,11 @@ loop_vsx:
|
||||
VADDUWM V11, V18, V8
|
||||
VADDUWM V15, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -408,9 +466,9 @@ loop_vsx:
|
||||
|
||||
done_vsx:
|
||||
// Increment counter by number of 64 byte blocks
|
||||
MOVD (CNT), R14
|
||||
MOVWZ (CNT), R14
|
||||
ADD BLOCKS, R14
|
||||
MOVD R14, (CNT)
|
||||
MOVWZ R14, (CNT)
|
||||
RET
|
||||
|
||||
tail_vsx:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
module chacha20poly1305/_asm
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
+9270
-2223
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -234,7 +234,7 @@ func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) {
|
||||
// Identifiers with the low five bits set indicate high-tag-number format
|
||||
// (two or more octets), which we don't support.
|
||||
if tag&0x1f == 0x1f {
|
||||
b.err = fmt.Errorf("cryptobyte: high-tag number identifier octects not supported: 0x%x", tag)
|
||||
b.err = fmt.Errorf("cryptobyte: high-tag number identifier octets not supported: 0x%x", tag)
|
||||
return
|
||||
}
|
||||
b.AddUint8(uint8(tag))
|
||||
|
||||
@@ -36,7 +36,7 @@ func ScalarBaseMult(dst, scalar *[32]byte) {
|
||||
curve := ecdh.X25519()
|
||||
priv, err := curve.NewPrivateKey(scalar[:])
|
||||
if err != nil {
|
||||
panic("curve25519: internal error: scalarBaseMult was not 32 bytes")
|
||||
panic("curve25519: " + err.Error())
|
||||
}
|
||||
copy(dst[:], priv.PublicKey().Bytes())
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module golang.org/x/crypto
|
||||
|
||||
go 1.20
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.21.0 // tagx:ignore
|
||||
golang.org/x/sys v0.23.0
|
||||
golang.org/x/term v0.23.0
|
||||
golang.org/x/net v0.43.0 // tagx:ignore
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/term v0.35.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.17.0 // indirect
|
||||
require golang.org/x/text v0.29.0 // indirect
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
module internal/poly1305/_asm
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright 2024 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 main
|
||||
|
||||
import (
|
||||
. "github.com/mmcloughlin/avo/build"
|
||||
. "github.com/mmcloughlin/avo/operand"
|
||||
. "github.com/mmcloughlin/avo/reg"
|
||||
_ "golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
//go:generate go run . -out ../sum_amd64.s -pkg poly1305
|
||||
|
||||
func main() {
|
||||
Package("golang.org/x/crypto/internal/poly1305")
|
||||
ConstraintExpr("gc,!purego")
|
||||
update()
|
||||
Generate()
|
||||
}
|
||||
|
||||
func update() {
|
||||
Implement("update")
|
||||
|
||||
Load(Param("state"), RDI)
|
||||
MOVQ(NewParamAddr("msg_base", 8), RSI)
|
||||
MOVQ(NewParamAddr("msg_len", 16), R15)
|
||||
|
||||
MOVQ(Mem{Base: DI}.Offset(0), R8) // h0
|
||||
MOVQ(Mem{Base: DI}.Offset(8), R9) // h1
|
||||
MOVQ(Mem{Base: DI}.Offset(16), R10) // h2
|
||||
MOVQ(Mem{Base: DI}.Offset(24), R11) // r0
|
||||
MOVQ(Mem{Base: DI}.Offset(32), R12) // r1
|
||||
|
||||
CMPQ(R15, Imm(16))
|
||||
JB(LabelRef("bytes_between_0_and_15"))
|
||||
|
||||
Label("loop")
|
||||
POLY1305_ADD(RSI, R8, R9, R10)
|
||||
|
||||
Label("multiply")
|
||||
POLY1305_MUL(R8, R9, R10, R11, R12, RBX, RCX, R13, R14)
|
||||
SUBQ(Imm(16), R15)
|
||||
CMPQ(R15, Imm(16))
|
||||
JAE(LabelRef("loop"))
|
||||
|
||||
Label("bytes_between_0_and_15")
|
||||
TESTQ(R15, R15)
|
||||
JZ(LabelRef("done"))
|
||||
MOVQ(U32(1), RBX)
|
||||
XORQ(RCX, RCX)
|
||||
XORQ(R13, R13)
|
||||
ADDQ(R15, RSI)
|
||||
|
||||
Label("flush_buffer")
|
||||
SHLQ(Imm(8), RBX, RCX)
|
||||
SHLQ(Imm(8), RBX)
|
||||
MOVB(Mem{Base: SI}.Offset(-1), R13B)
|
||||
XORQ(R13, RBX)
|
||||
DECQ(RSI)
|
||||
DECQ(R15)
|
||||
JNZ(LabelRef("flush_buffer"))
|
||||
|
||||
ADDQ(RBX, R8)
|
||||
ADCQ(RCX, R9)
|
||||
ADCQ(Imm(0), R10)
|
||||
MOVQ(U32(16), R15)
|
||||
JMP(LabelRef("multiply"))
|
||||
|
||||
Label("done")
|
||||
MOVQ(R8, Mem{Base: DI}.Offset(0))
|
||||
MOVQ(R9, Mem{Base: DI}.Offset(8))
|
||||
MOVQ(R10, Mem{Base: DI}.Offset(16))
|
||||
RET()
|
||||
}
|
||||
|
||||
func POLY1305_ADD(msg, h0, h1, h2 GPPhysical) {
|
||||
ADDQ(Mem{Base: msg}.Offset(0), h0)
|
||||
ADCQ(Mem{Base: msg}.Offset(8), h1)
|
||||
ADCQ(Imm(1), h2)
|
||||
LEAQ(Mem{Base: msg}.Offset(16), msg)
|
||||
}
|
||||
|
||||
func POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3 GPPhysical) {
|
||||
MOVQ(r0, RAX)
|
||||
MULQ(h0)
|
||||
MOVQ(RAX, t0)
|
||||
MOVQ(RDX, t1)
|
||||
MOVQ(r0, RAX)
|
||||
MULQ(h1)
|
||||
ADDQ(RAX, t1)
|
||||
ADCQ(Imm(0), RDX)
|
||||
MOVQ(r0, t2)
|
||||
IMULQ(h2, t2)
|
||||
ADDQ(RDX, t2)
|
||||
|
||||
MOVQ(r1, RAX)
|
||||
MULQ(h0)
|
||||
ADDQ(RAX, t1)
|
||||
ADCQ(Imm(0), RDX)
|
||||
MOVQ(RDX, h0)
|
||||
MOVQ(r1, t3)
|
||||
IMULQ(h2, t3)
|
||||
MOVQ(r1, RAX)
|
||||
MULQ(h1)
|
||||
ADDQ(RAX, t2)
|
||||
ADCQ(RDX, t3)
|
||||
ADDQ(h0, t2)
|
||||
ADCQ(Imm(0), t3)
|
||||
|
||||
MOVQ(t0, h0)
|
||||
MOVQ(t1, h1)
|
||||
MOVQ(t2, h2)
|
||||
ANDQ(Imm(3), h2)
|
||||
MOVQ(t2, t0)
|
||||
ANDQ(I32(-4), t0)
|
||||
ADDQ(t0, h0)
|
||||
ADCQ(t3, h1)
|
||||
ADCQ(Imm(0), h2)
|
||||
SHRQ(Imm(2), t3, t2)
|
||||
SHRQ(Imm(2), t3)
|
||||
ADDQ(t2, h0)
|
||||
ADCQ(t3, h1)
|
||||
ADCQ(Imm(0), h2)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego
|
||||
//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !s390x) || !gc || purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
@@ -1,108 +1,93 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Code generated by command: go run sum_amd64_asm.go -out ../sum_amd64.s -pkg poly1305. DO NOT EDIT.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2) \
|
||||
ADDQ 0(msg), h0; \
|
||||
ADCQ 8(msg), h1; \
|
||||
ADCQ $1, h2; \
|
||||
LEAQ 16(msg), msg
|
||||
|
||||
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h0; \
|
||||
MOVQ AX, t0; \
|
||||
MOVQ DX, t1; \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ r0, t2; \
|
||||
IMULQ h2, t2; \
|
||||
ADDQ DX, t2; \
|
||||
\
|
||||
MOVQ r1, AX; \
|
||||
MULQ h0; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ DX, h0; \
|
||||
MOVQ r1, t3; \
|
||||
IMULQ h2, t3; \
|
||||
MOVQ r1, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t2; \
|
||||
ADCQ DX, t3; \
|
||||
ADDQ h0, t2; \
|
||||
ADCQ $0, t3; \
|
||||
\
|
||||
MOVQ t0, h0; \
|
||||
MOVQ t1, h1; \
|
||||
MOVQ t2, h2; \
|
||||
ANDQ $3, h2; \
|
||||
MOVQ t2, t0; \
|
||||
ANDQ $0xFFFFFFFFFFFFFFFC, t0; \
|
||||
ADDQ t0, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2; \
|
||||
SHRQ $2, t3, t2; \
|
||||
SHRQ $2, t3; \
|
||||
ADDQ t2, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2
|
||||
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
// func update(state *macState, 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
|
||||
MOVQ (DI), R8
|
||||
MOVQ 8(DI), R9
|
||||
MOVQ 16(DI), R10
|
||||
MOVQ 24(DI), R11
|
||||
MOVQ 32(DI), R12
|
||||
CMPQ R15, $0x10
|
||||
JB bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
POLY1305_ADD(SI, R8, R9, R10)
|
||||
ADDQ (SI), R8
|
||||
ADCQ 8(SI), R9
|
||||
ADCQ $0x01, R10
|
||||
LEAQ 16(SI), SI
|
||||
|
||||
multiply:
|
||||
POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
|
||||
SUBQ $16, R15
|
||||
CMPQ R15, $16
|
||||
JAE loop
|
||||
MOVQ R11, AX
|
||||
MULQ R8
|
||||
MOVQ AX, BX
|
||||
MOVQ DX, CX
|
||||
MOVQ R11, AX
|
||||
MULQ R9
|
||||
ADDQ AX, CX
|
||||
ADCQ $0x00, DX
|
||||
MOVQ R11, R13
|
||||
IMULQ R10, R13
|
||||
ADDQ DX, R13
|
||||
MOVQ R12, AX
|
||||
MULQ R8
|
||||
ADDQ AX, CX
|
||||
ADCQ $0x00, DX
|
||||
MOVQ DX, R8
|
||||
MOVQ R12, R14
|
||||
IMULQ R10, R14
|
||||
MOVQ R12, AX
|
||||
MULQ R9
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R14
|
||||
ADDQ R8, R13
|
||||
ADCQ $0x00, R14
|
||||
MOVQ BX, R8
|
||||
MOVQ CX, R9
|
||||
MOVQ R13, R10
|
||||
ANDQ $0x03, R10
|
||||
MOVQ R13, BX
|
||||
ANDQ $-4, BX
|
||||
ADDQ BX, R8
|
||||
ADCQ R14, R9
|
||||
ADCQ $0x00, R10
|
||||
SHRQ $0x02, R14, R13
|
||||
SHRQ $0x02, R14
|
||||
ADDQ R13, R8
|
||||
ADCQ R14, R9
|
||||
ADCQ $0x00, R10
|
||||
SUBQ $0x10, R15
|
||||
CMPQ R15, $0x10
|
||||
JAE loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
TESTQ R15, R15
|
||||
JZ done
|
||||
MOVQ $1, BX
|
||||
MOVQ $0x00000001, BX
|
||||
XORQ CX, CX
|
||||
XORQ R13, R13
|
||||
ADDQ R15, SI
|
||||
|
||||
flush_buffer:
|
||||
SHLQ $8, BX, CX
|
||||
SHLQ $8, BX
|
||||
SHLQ $0x08, BX, CX
|
||||
SHLQ $0x08, BX
|
||||
MOVB -1(SI), R13
|
||||
XORQ R13, BX
|
||||
DECQ SI
|
||||
DECQ R15
|
||||
JNZ flush_buffer
|
||||
|
||||
ADDQ BX, R8
|
||||
ADCQ CX, R9
|
||||
ADCQ $0, R10
|
||||
MOVQ $16, R15
|
||||
ADCQ $0x00, R10
|
||||
MOVQ $0x00000010, R15
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVQ R8, 0(DI)
|
||||
MOVQ R8, (DI)
|
||||
MOVQ R9, 8(DI)
|
||||
MOVQ R10, 16(DI)
|
||||
RET
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le)
|
||||
|
||||
package poly1305
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
// func update(state *macState, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVV state+0(FP), R4
|
||||
MOVV msg_base+8(FP), R5
|
||||
MOVV msg_len+16(FP), R6
|
||||
|
||||
MOVV $0x10, R7
|
||||
|
||||
MOVV (R4), R8 // h0
|
||||
MOVV 8(R4), R9 // h1
|
||||
MOVV 16(R4), R10 // h2
|
||||
MOVV 24(R4), R11 // r0
|
||||
MOVV 32(R4), R12 // r1
|
||||
|
||||
BLT R6, R7, bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
MOVV (R5), R14 // msg[0:8]
|
||||
MOVV 8(R5), R16 // msg[8:16]
|
||||
ADDV R14, R8, R8 // h0 (x1 + y1 = z1', if z1' < x1 then z1' overflow)
|
||||
ADDV R16, R9, R27
|
||||
SGTU R14, R8, R24 // h0.carry
|
||||
SGTU R9, R27, R28
|
||||
ADDV R27, R24, R9 // h1
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24 // h1.carry
|
||||
ADDV $0x01, R24, R24
|
||||
ADDV R10, R24, R10 // h2
|
||||
|
||||
ADDV $16, R5, R5 // msg = msg[16:]
|
||||
|
||||
multiply:
|
||||
MULV R8, R11, R14 // h0r0.lo
|
||||
MULHVU R8, R11, R15 // h0r0.hi
|
||||
MULV R9, R11, R13 // h1r0.lo
|
||||
MULHVU R9, R11, R16 // h1r0.hi
|
||||
ADDV R13, R15, R15
|
||||
SGTU R13, R15, R24
|
||||
ADDV R24, R16, R16
|
||||
MULV R10, R11, R25
|
||||
ADDV R16, R25, R25
|
||||
MULV R8, R12, R13 // h0r1.lo
|
||||
MULHVU R8, R12, R16 // h0r1.hi
|
||||
ADDV R13, R15, R15
|
||||
SGTU R13, R15, R24
|
||||
ADDV R24, R16, R16
|
||||
MOVV R16, R8
|
||||
MULV R10, R12, R26 // h2r1
|
||||
MULV R9, R12, R13 // h1r1.lo
|
||||
MULHVU R9, R12, R16 // h1r1.hi
|
||||
ADDV R13, R25, R25
|
||||
ADDV R16, R26, R27
|
||||
SGTU R13, R25, R24
|
||||
ADDV R27, R24, R26
|
||||
ADDV R8, R25, R25
|
||||
SGTU R8, R25, R24
|
||||
ADDV R24, R26, R26
|
||||
AND $3, R25, R10
|
||||
AND $-4, R25, R17
|
||||
ADDV R17, R14, R8
|
||||
ADDV R26, R15, R27
|
||||
SGTU R17, R8, R24
|
||||
SGTU R26, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R24, R10, R10
|
||||
SLLV $62, R26, R27
|
||||
SRLV $2, R25, R28
|
||||
SRLV $2, R26, R26
|
||||
OR R27, R28, R25
|
||||
ADDV R25, R8, R8
|
||||
ADDV R26, R9, R27
|
||||
SGTU R25, R8, R24
|
||||
SGTU R26, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R24, R10, R10
|
||||
|
||||
SUBV $16, R6, R6
|
||||
BGE R6, R7, loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
BEQ R6, R0, done
|
||||
MOVV $1, R14
|
||||
XOR R15, R15
|
||||
ADDV R6, R5, R5
|
||||
|
||||
flush_buffer:
|
||||
MOVBU -1(R5), R25
|
||||
SRLV $56, R14, R24
|
||||
SLLV $8, R15, R28
|
||||
SLLV $8, R14, R14
|
||||
OR R24, R28, R15
|
||||
XOR R25, R14, R14
|
||||
SUBV $1, R6, R6
|
||||
SUBV $1, R5, R5
|
||||
BNE R6, R0, flush_buffer
|
||||
|
||||
ADDV R14, R8, R8
|
||||
SGTU R14, R8, R24
|
||||
ADDV R15, R9, R27
|
||||
SGTU R15, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R10, R24, R10
|
||||
|
||||
MOVV $16, R6
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVV R8, (R4)
|
||||
MOVV R9, 8(R4)
|
||||
MOVV R10, 16(R4)
|
||||
RET
|
||||
@@ -1,47 +0,0 @@
|
||||
// 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.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
package poly1305
|
||||
|
||||
//go:noescape
|
||||
func update(state *macState, msg []byte)
|
||||
|
||||
// mac is a wrapper for macGeneric that redirects calls that would have gone to
|
||||
// updateGeneric to update.
|
||||
//
|
||||
// Its Write and Sum methods are otherwise identical to the macGeneric ones, but
|
||||
// using function pointers would carry a major performance cost.
|
||||
type mac struct{ macGeneric }
|
||||
|
||||
func (h *mac) Write(p []byte) (int, error) {
|
||||
nn := len(p)
|
||||
if h.offset > 0 {
|
||||
n := copy(h.buffer[h.offset:], p)
|
||||
if h.offset+n < TagSize {
|
||||
h.offset += n
|
||||
return nn, nil
|
||||
}
|
||||
p = p[n:]
|
||||
h.offset = 0
|
||||
update(&h.macState, h.buffer[:])
|
||||
}
|
||||
if n := len(p) - (len(p) % TagSize); n > 0 {
|
||||
update(&h.macState, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
}
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (h *mac) Sum(out *[16]byte) {
|
||||
state := h.macState
|
||||
if h.offset > 0 {
|
||||
update(&state, h.buffer[:h.offset])
|
||||
}
|
||||
finalize(out, &state.h, &state.s)
|
||||
}
|
||||
@@ -2,15 +2,25 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// This was ported from the amd64 implementation.
|
||||
|
||||
#ifdef GOARCH_ppc64le
|
||||
#define LE_MOVD MOVD
|
||||
#define LE_MOVWZ MOVWZ
|
||||
#define LE_MOVHZ MOVHZ
|
||||
#else
|
||||
#define LE_MOVD MOVDBR
|
||||
#define LE_MOVWZ MOVWBR
|
||||
#define LE_MOVHZ MOVHBR
|
||||
#endif
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \
|
||||
MOVD (msg), t0; \
|
||||
MOVD 8(msg), t1; \
|
||||
LE_MOVD (msg)( R0), t0; \
|
||||
LE_MOVD (msg)(R24), t1; \
|
||||
MOVD $1, t2; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t1, h1, h1; \
|
||||
@@ -50,10 +60,6 @@
|
||||
ADDE t3, h1, h1; \
|
||||
ADDZE h2
|
||||
|
||||
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
|
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
|
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16
|
||||
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVD state+0(FP), R3
|
||||
@@ -66,6 +72,8 @@ TEXT ·update(SB), $0-32
|
||||
MOVD 24(R3), R11 // r0
|
||||
MOVD 32(R3), R12 // r1
|
||||
|
||||
MOVD $8, R24
|
||||
|
||||
CMP R5, $16
|
||||
BLT bytes_between_0_and_15
|
||||
|
||||
@@ -94,7 +102,7 @@ flush_buffer:
|
||||
|
||||
// Greater than 8 -- load the rightmost remaining bytes in msg
|
||||
// and put into R17 (h1)
|
||||
MOVD (R4)(R21), R17
|
||||
LE_MOVD (R4)(R21), R17
|
||||
MOVD $16, R22
|
||||
|
||||
// Find the offset to those bytes
|
||||
@@ -118,7 +126,7 @@ just1:
|
||||
BLT less8
|
||||
|
||||
// Exactly 8
|
||||
MOVD (R4), R16
|
||||
LE_MOVD (R4), R16
|
||||
|
||||
CMP R17, $0
|
||||
|
||||
@@ -133,7 +141,7 @@ less8:
|
||||
MOVD $0, R22 // shift count
|
||||
CMP R5, $4
|
||||
BLT less4
|
||||
MOVWZ (R4), R16
|
||||
LE_MOVWZ (R4), R16
|
||||
ADD $4, R4
|
||||
ADD $-4, R5
|
||||
MOVD $32, R22
|
||||
@@ -141,7 +149,7 @@ less8:
|
||||
less4:
|
||||
CMP R5, $2
|
||||
BLT less2
|
||||
MOVHZ (R4), R21
|
||||
LE_MOVHZ (R4), R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
ADD $16, R22
|
||||
@@ -123,24 +123,12 @@ func TestSigning(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// We use this to make test keys, so that they aren't all the same.
|
||||
type quickRand byte
|
||||
|
||||
func (qr *quickRand) Read(p []byte) (int, error) {
|
||||
for i := range p {
|
||||
p[i] = byte(*qr)
|
||||
}
|
||||
*qr++
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestMultiSign(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping long test in -short mode")
|
||||
}
|
||||
|
||||
zero := quickRand(0)
|
||||
config := packet.Config{Rand: &zero}
|
||||
var config packet.Config
|
||||
|
||||
for nKeys := 0; nKeys < 4; nKeys++ {
|
||||
nextTest:
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ type pbeCipher interface {
|
||||
create(key []byte) (cipher.Block, error)
|
||||
// deriveKey returns a key derived from the given password and salt.
|
||||
deriveKey(salt, password []byte, iterations int) []byte
|
||||
// deriveKey returns an IV derived from the given password and salt.
|
||||
// deriveIV returns an IV derived from the given password and salt.
|
||||
deriveIV(salt, password []byte, iterations int) []byte
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
module salsa20/salsa/_asm
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
@@ -0,0 +1,932 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/mmcloughlin/avo/build"
|
||||
"github.com/mmcloughlin/avo/ir"
|
||||
. "github.com/mmcloughlin/avo/operand"
|
||||
. "github.com/mmcloughlin/avo/reg"
|
||||
_ "golang.org/x/crypto/salsa20/salsa"
|
||||
)
|
||||
|
||||
//go:generate go run . -out ../salsa20_amd64.s -pkg salsa
|
||||
|
||||
func main() {
|
||||
Package("golang.org/x/crypto/salsa20/salsa")
|
||||
ConstraintExpr("amd64,!purego,gc")
|
||||
salsa2020XORKeyStream()
|
||||
Generate()
|
||||
}
|
||||
|
||||
func salsa2020XORKeyStream() {
|
||||
Implement("salsa2020XORKeyStream")
|
||||
Attributes(0)
|
||||
AllocLocal(456) // frame = 424 + 32 byte alignment
|
||||
Comment("This needs up to 64 bytes at 360(R12); hence the non-obvious frame size.")
|
||||
|
||||
Load(Param("out"), RDI)
|
||||
Load(Param("in"), RSI)
|
||||
Load(Param("n"), RDX)
|
||||
Load(Param("nonce"), RCX)
|
||||
Load(Param("key"), R8)
|
||||
|
||||
MOVQ(RSP, R12)
|
||||
ADDQ(Imm(31), R12)
|
||||
ANDQ(I32(^31), R12)
|
||||
|
||||
MOVQ(RDX, R9)
|
||||
MOVQ(RCX, RDX)
|
||||
MOVQ(R8, R10)
|
||||
CMPQ(R9, Imm(0))
|
||||
JBE(LabelRef("DONE"))
|
||||
|
||||
START()
|
||||
BYTESATLEAST256()
|
||||
MAINLOOP1()
|
||||
BYTESBETWEEN1AND255()
|
||||
NOCOPY()
|
||||
MAINLOOP2()
|
||||
|
||||
Label("BYTESATLEAST64")
|
||||
Label("DONE")
|
||||
RET()
|
||||
Label("BYTESATLEAST65")
|
||||
SUBQ(Imm(64), R9)
|
||||
ADDQ(Imm(64), RDI)
|
||||
ADDQ(Imm(64), RSI)
|
||||
JMP(LabelRef("BYTESBETWEEN1AND255"))
|
||||
}
|
||||
|
||||
func START() {
|
||||
Label("START")
|
||||
MOVL(Mem{Base: R10}.Offset(20), ECX)
|
||||
MOVL(Mem{Base: R10}.Offset(0), R8L)
|
||||
MOVL(Mem{Base: EDX}.Offset(0), EAX)
|
||||
MOVL(Mem{Base: R10}.Offset(16), R11L)
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(0))
|
||||
MOVL(R8L, Mem{Base: R12}.Offset(4))
|
||||
MOVL(EAX, Mem{Base: R12}.Offset(8))
|
||||
MOVL(R11L, Mem{Base: R12}.Offset(12))
|
||||
MOVL(Mem{Base: EDX}.Offset(8), ECX)
|
||||
MOVL(Mem{Base: R10}.Offset(24), R8L)
|
||||
MOVL(Mem{Base: R10}.Offset(4), EAX)
|
||||
MOVL(Mem{Base: EDX}.Offset(4), R11L)
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(16))
|
||||
MOVL(R8L, Mem{Base: R12}.Offset(20))
|
||||
MOVL(EAX, Mem{Base: R12}.Offset(24))
|
||||
MOVL(R11L, Mem{Base: R12}.Offset(28))
|
||||
MOVL(Mem{Base: EDX}.Offset(12), ECX)
|
||||
MOVL(Mem{Base: R10}.Offset(12), EDX)
|
||||
MOVL(Mem{Base: R10}.Offset(28), R8L)
|
||||
MOVL(Mem{Base: R10}.Offset(8), EAX)
|
||||
MOVL(EDX, Mem{Base: R12}.Offset(32))
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(36))
|
||||
MOVL(R8L, Mem{Base: R12}.Offset(40))
|
||||
MOVL(EAX, Mem{Base: R12}.Offset(44))
|
||||
MOVQ(Imm(1634760805), RDX)
|
||||
MOVQ(Imm(857760878), RCX)
|
||||
MOVQ(Imm(2036477234), R8)
|
||||
MOVQ(Imm(1797285236), RAX)
|
||||
MOVL(EDX, Mem{Base: R12}.Offset(48))
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(52))
|
||||
MOVL(R8L, Mem{Base: R12}.Offset(56))
|
||||
MOVL(EAX, Mem{Base: R12}.Offset(60))
|
||||
CMPQ(R9, U32(256))
|
||||
JB(LabelRef("BYTESBETWEEN1AND255"))
|
||||
MOVOA(Mem{Base: R12}.Offset(48), X0)
|
||||
PSHUFL(Imm(0x55), X0, X1)
|
||||
PSHUFL(Imm(0xAA), X0, X2)
|
||||
PSHUFL(Imm(0xFF), X0, X3)
|
||||
PSHUFL(Imm(0x00), X0, X0)
|
||||
MOVOA(X1, Mem{Base: R12}.Offset(64))
|
||||
MOVOA(X2, Mem{Base: R12}.Offset(80))
|
||||
MOVOA(X3, Mem{Base: R12}.Offset(96))
|
||||
MOVOA(X0, Mem{Base: R12}.Offset(112))
|
||||
MOVOA(Mem{Base: R12}.Offset(0), X0)
|
||||
PSHUFL(Imm(0xAA), X0, X1)
|
||||
PSHUFL(Imm(0xFF), X0, X2)
|
||||
PSHUFL(Imm(0x00), X0, X3)
|
||||
PSHUFL(Imm(0x55), X0, X0)
|
||||
MOVOA(X1, Mem{Base: R12}.Offset(128))
|
||||
MOVOA(X2, Mem{Base: R12}.Offset(144))
|
||||
MOVOA(X3, Mem{Base: R12}.Offset(160))
|
||||
MOVOA(X0, Mem{Base: R12}.Offset(176))
|
||||
MOVOA(Mem{Base: R12}.Offset(16), X0)
|
||||
PSHUFL(Imm(0xFF), X0, X1)
|
||||
PSHUFL(Imm(0x55), X0, X2)
|
||||
PSHUFL(Imm(0xAA), X0, X0)
|
||||
MOVOA(X1, Mem{Base: R12}.Offset(192))
|
||||
MOVOA(X2, Mem{Base: R12}.Offset(208))
|
||||
MOVOA(X0, Mem{Base: R12}.Offset(224))
|
||||
MOVOA(Mem{Base: R12}.Offset(32), X0)
|
||||
PSHUFL(Imm(0x00), X0, X1)
|
||||
PSHUFL(Imm(0xAA), X0, X2)
|
||||
PSHUFL(Imm(0xFF), X0, X0)
|
||||
MOVOA(X1, Mem{Base: R12}.Offset(240))
|
||||
MOVOA(X2, Mem{Base: R12}.Offset(256))
|
||||
MOVOA(X0, Mem{Base: R12}.Offset(272))
|
||||
|
||||
}
|
||||
|
||||
func BYTESATLEAST256() {
|
||||
Label("BYTESATLEAST256")
|
||||
MOVL(Mem{Base: R12}.Offset(16), EDX)
|
||||
MOVL(Mem{Base: R12}.Offset(36), ECX)
|
||||
MOVL(EDX, Mem{Base: R12}.Offset(288))
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(304))
|
||||
SHLQ(Imm(32), RCX)
|
||||
ADDQ(RCX, RDX)
|
||||
ADDQ(Imm(1), RDX)
|
||||
MOVQ(RDX, RCX)
|
||||
SHRQ(Imm(32), RCX)
|
||||
MOVL(EDX, Mem{Base: R12}.Offset(292))
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(308))
|
||||
ADDQ(Imm(1), RDX)
|
||||
MOVQ(RDX, RCX)
|
||||
SHRQ(Imm(32), RCX)
|
||||
MOVL(EDX, Mem{Base: R12}.Offset(296))
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(312))
|
||||
ADDQ(Imm(1), RDX)
|
||||
MOVQ(RDX, RCX)
|
||||
SHRQ(Imm(32), RCX)
|
||||
MOVL(EDX, Mem{Base: R12}.Offset(300))
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(316))
|
||||
ADDQ(Imm(1), RDX)
|
||||
MOVQ(RDX, RCX)
|
||||
SHRQ(Imm(32), RCX)
|
||||
MOVL(EDX, Mem{Base: R12}.Offset(16))
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(36))
|
||||
MOVQ(R9, Mem{Base: R12}.Offset(352))
|
||||
MOVQ(U32(20), RDX)
|
||||
MOVOA(Mem{Base: R12}.Offset(64), X0)
|
||||
MOVOA(Mem{Base: R12}.Offset(80), X1)
|
||||
MOVOA(Mem{Base: R12}.Offset(96), X2)
|
||||
MOVOA(Mem{Base: R12}.Offset(256), X3)
|
||||
MOVOA(Mem{Base: R12}.Offset(272), X4)
|
||||
MOVOA(Mem{Base: R12}.Offset(128), X5)
|
||||
MOVOA(Mem{Base: R12}.Offset(144), X6)
|
||||
MOVOA(Mem{Base: R12}.Offset(176), X7)
|
||||
MOVOA(Mem{Base: R12}.Offset(192), X8)
|
||||
MOVOA(Mem{Base: R12}.Offset(208), X9)
|
||||
MOVOA(Mem{Base: R12}.Offset(224), X10)
|
||||
MOVOA(Mem{Base: R12}.Offset(304), X11)
|
||||
MOVOA(Mem{Base: R12}.Offset(112), X12)
|
||||
MOVOA(Mem{Base: R12}.Offset(160), X13)
|
||||
MOVOA(Mem{Base: R12}.Offset(240), X14)
|
||||
MOVOA(Mem{Base: R12}.Offset(288), X15)
|
||||
}
|
||||
|
||||
func MAINLOOP1() {
|
||||
Label("MAINLOOP1")
|
||||
MOVOA(X1, Mem{Base: R12}.Offset(320))
|
||||
MOVOA(X2, Mem{Base: R12}.Offset(336))
|
||||
MOVOA(X13, X1)
|
||||
PADDL(X12, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(7), X1)
|
||||
PXOR(X1, X14)
|
||||
PSRLL(Imm(25), X2)
|
||||
PXOR(X2, X14)
|
||||
MOVOA(X7, X1)
|
||||
PADDL(X0, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(7), X1)
|
||||
PXOR(X1, X11)
|
||||
PSRLL(Imm(25), X2)
|
||||
PXOR(X2, X11)
|
||||
MOVOA(X12, X1)
|
||||
PADDL(X14, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(9), X1)
|
||||
PXOR(X1, X15)
|
||||
PSRLL(Imm(23), X2)
|
||||
PXOR(X2, X15)
|
||||
MOVOA(X0, X1)
|
||||
PADDL(X11, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(9), X1)
|
||||
PXOR(X1, X9)
|
||||
PSRLL(Imm(23), X2)
|
||||
PXOR(X2, X9)
|
||||
MOVOA(X14, X1)
|
||||
PADDL(X15, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(13), X1)
|
||||
PXOR(X1, X13)
|
||||
PSRLL(Imm(19), X2)
|
||||
PXOR(X2, X13)
|
||||
MOVOA(X11, X1)
|
||||
PADDL(X9, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(13), X1)
|
||||
PXOR(X1, X7)
|
||||
PSRLL(Imm(19), X2)
|
||||
PXOR(X2, X7)
|
||||
MOVOA(X15, X1)
|
||||
PADDL(X13, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(18), X1)
|
||||
PXOR(X1, X12)
|
||||
PSRLL(Imm(14), X2)
|
||||
PXOR(X2, X12)
|
||||
MOVOA(Mem{Base: R12}.Offset(320), X1)
|
||||
MOVOA(X12, Mem{Base: R12}.Offset(320))
|
||||
MOVOA(X9, X2)
|
||||
PADDL(X7, X2)
|
||||
MOVOA(X2, X12)
|
||||
PSLLL(Imm(18), X2)
|
||||
PXOR(X2, X0)
|
||||
PSRLL(Imm(14), X12)
|
||||
PXOR(X12, X0)
|
||||
MOVOA(X5, X2)
|
||||
PADDL(X1, X2)
|
||||
MOVOA(X2, X12)
|
||||
PSLLL(Imm(7), X2)
|
||||
PXOR(X2, X3)
|
||||
PSRLL(Imm(25), X12)
|
||||
PXOR(X12, X3)
|
||||
MOVOA(Mem{Base: R12}.Offset(336), X2)
|
||||
MOVOA(X0, Mem{Base: R12}.Offset(336))
|
||||
MOVOA(X6, X0)
|
||||
PADDL(X2, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(7), X0)
|
||||
PXOR(X0, X4)
|
||||
PSRLL(Imm(25), X12)
|
||||
PXOR(X12, X4)
|
||||
MOVOA(X1, X0)
|
||||
PADDL(X3, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(9), X0)
|
||||
PXOR(X0, X10)
|
||||
PSRLL(Imm(23), X12)
|
||||
PXOR(X12, X10)
|
||||
MOVOA(X2, X0)
|
||||
PADDL(X4, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(9), X0)
|
||||
PXOR(X0, X8)
|
||||
PSRLL(Imm(23), X12)
|
||||
PXOR(X12, X8)
|
||||
MOVOA(X3, X0)
|
||||
PADDL(X10, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(13), X0)
|
||||
PXOR(X0, X5)
|
||||
PSRLL(Imm(19), X12)
|
||||
PXOR(X12, X5)
|
||||
MOVOA(X4, X0)
|
||||
PADDL(X8, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(13), X0)
|
||||
PXOR(X0, X6)
|
||||
PSRLL(Imm(19), X12)
|
||||
PXOR(X12, X6)
|
||||
MOVOA(X10, X0)
|
||||
PADDL(X5, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(18), X0)
|
||||
PXOR(X0, X1)
|
||||
PSRLL(Imm(14), X12)
|
||||
PXOR(X12, X1)
|
||||
MOVOA(Mem{Base: R12}.Offset(320), X0)
|
||||
MOVOA(X1, Mem{Base: R12}.Offset(320))
|
||||
MOVOA(X4, X1)
|
||||
PADDL(X0, X1)
|
||||
MOVOA(X1, X12)
|
||||
PSLLL(Imm(7), X1)
|
||||
PXOR(X1, X7)
|
||||
PSRLL(Imm(25), X12)
|
||||
PXOR(X12, X7)
|
||||
MOVOA(X8, X1)
|
||||
PADDL(X6, X1)
|
||||
MOVOA(X1, X12)
|
||||
PSLLL(Imm(18), X1)
|
||||
PXOR(X1, X2)
|
||||
PSRLL(Imm(14), X12)
|
||||
PXOR(X12, X2)
|
||||
MOVOA(Mem{Base: R12}.Offset(336), X12)
|
||||
MOVOA(X2, Mem{Base: R12}.Offset(336))
|
||||
MOVOA(X14, X1)
|
||||
PADDL(X12, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(7), X1)
|
||||
PXOR(X1, X5)
|
||||
PSRLL(Imm(25), X2)
|
||||
PXOR(X2, X5)
|
||||
MOVOA(X0, X1)
|
||||
PADDL(X7, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(9), X1)
|
||||
PXOR(X1, X10)
|
||||
PSRLL(Imm(23), X2)
|
||||
PXOR(X2, X10)
|
||||
MOVOA(X12, X1)
|
||||
PADDL(X5, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(9), X1)
|
||||
PXOR(X1, X8)
|
||||
PSRLL(Imm(23), X2)
|
||||
PXOR(X2, X8)
|
||||
MOVOA(X7, X1)
|
||||
PADDL(X10, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(13), X1)
|
||||
PXOR(X1, X4)
|
||||
PSRLL(Imm(19), X2)
|
||||
PXOR(X2, X4)
|
||||
MOVOA(X5, X1)
|
||||
PADDL(X8, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(13), X1)
|
||||
PXOR(X1, X14)
|
||||
PSRLL(Imm(19), X2)
|
||||
PXOR(X2, X14)
|
||||
MOVOA(X10, X1)
|
||||
PADDL(X4, X1)
|
||||
MOVOA(X1, X2)
|
||||
PSLLL(Imm(18), X1)
|
||||
PXOR(X1, X0)
|
||||
PSRLL(Imm(14), X2)
|
||||
PXOR(X2, X0)
|
||||
MOVOA(Mem{Base: R12}.Offset(320), X1)
|
||||
MOVOA(X0, Mem{Base: R12}.Offset(320))
|
||||
MOVOA(X8, X0)
|
||||
PADDL(X14, X0)
|
||||
MOVOA(X0, X2)
|
||||
PSLLL(Imm(18), X0)
|
||||
PXOR(X0, X12)
|
||||
PSRLL(Imm(14), X2)
|
||||
PXOR(X2, X12)
|
||||
MOVOA(X11, X0)
|
||||
PADDL(X1, X0)
|
||||
MOVOA(X0, X2)
|
||||
PSLLL(Imm(7), X0)
|
||||
PXOR(X0, X6)
|
||||
PSRLL(Imm(25), X2)
|
||||
PXOR(X2, X6)
|
||||
MOVOA(Mem{Base: R12}.Offset(336), X2)
|
||||
MOVOA(X12, Mem{Base: R12}.Offset(336))
|
||||
MOVOA(X3, X0)
|
||||
PADDL(X2, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(7), X0)
|
||||
PXOR(X0, X13)
|
||||
PSRLL(Imm(25), X12)
|
||||
PXOR(X12, X13)
|
||||
MOVOA(X1, X0)
|
||||
PADDL(X6, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(9), X0)
|
||||
PXOR(X0, X15)
|
||||
PSRLL(Imm(23), X12)
|
||||
PXOR(X12, X15)
|
||||
MOVOA(X2, X0)
|
||||
PADDL(X13, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(9), X0)
|
||||
PXOR(X0, X9)
|
||||
PSRLL(Imm(23), X12)
|
||||
PXOR(X12, X9)
|
||||
MOVOA(X6, X0)
|
||||
PADDL(X15, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(13), X0)
|
||||
PXOR(X0, X11)
|
||||
PSRLL(Imm(19), X12)
|
||||
PXOR(X12, X11)
|
||||
MOVOA(X13, X0)
|
||||
PADDL(X9, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(13), X0)
|
||||
PXOR(X0, X3)
|
||||
PSRLL(Imm(19), X12)
|
||||
PXOR(X12, X3)
|
||||
MOVOA(X15, X0)
|
||||
PADDL(X11, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(18), X0)
|
||||
PXOR(X0, X1)
|
||||
PSRLL(Imm(14), X12)
|
||||
PXOR(X12, X1)
|
||||
MOVOA(X9, X0)
|
||||
PADDL(X3, X0)
|
||||
MOVOA(X0, X12)
|
||||
PSLLL(Imm(18), X0)
|
||||
PXOR(X0, X2)
|
||||
PSRLL(Imm(14), X12)
|
||||
PXOR(X12, X2)
|
||||
MOVOA(Mem{Base: R12}.Offset(320), X12)
|
||||
MOVOA(Mem{Base: R12}.Offset(336), X0)
|
||||
SUBQ(Imm(2), RDX)
|
||||
JA(LabelRef("MAINLOOP1"))
|
||||
PADDL(Mem{Base: R12}.Offset(112), X12)
|
||||
PADDL(Mem{Base: R12}.Offset(176), X7)
|
||||
PADDL(Mem{Base: R12}.Offset(224), X10)
|
||||
PADDL(Mem{Base: R12}.Offset(272), X4)
|
||||
MOVD(X12, EDX)
|
||||
MOVD(X7, ECX)
|
||||
MOVD(X10, R8)
|
||||
MOVD(X4, R9)
|
||||
PSHUFL(Imm(0x39), X12, X12)
|
||||
PSHUFL(Imm(0x39), X7, X7)
|
||||
PSHUFL(Imm(0x39), X10, X10)
|
||||
PSHUFL(Imm(0x39), X4, X4)
|
||||
XORL(Mem{Base: SI}.Offset(0), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(4), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(8), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(12), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(0))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(4))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(8))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(12))
|
||||
MOVD(X12, EDX)
|
||||
MOVD(X7, ECX)
|
||||
MOVD(X10, R8)
|
||||
MOVD(X4, R9)
|
||||
PSHUFL(Imm(0x39), X12, X12)
|
||||
PSHUFL(Imm(0x39), X7, X7)
|
||||
PSHUFL(Imm(0x39), X10, X10)
|
||||
PSHUFL(Imm(0x39), X4, X4)
|
||||
XORL(Mem{Base: SI}.Offset(64), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(68), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(72), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(76), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(64))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(68))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(72))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(76))
|
||||
MOVD(X12, EDX)
|
||||
MOVD(X7, ECX)
|
||||
MOVD(X10, R8)
|
||||
MOVD(X4, R9)
|
||||
PSHUFL(Imm(0x39), X12, X12)
|
||||
PSHUFL(Imm(0x39), X7, X7)
|
||||
PSHUFL(Imm(0x39), X10, X10)
|
||||
PSHUFL(Imm(0x39), X4, X4)
|
||||
XORL(Mem{Base: SI}.Offset(128), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(132), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(136), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(140), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(128))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(132))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(136))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(140))
|
||||
MOVD(X12, EDX)
|
||||
MOVD(X7, ECX)
|
||||
MOVD(X10, R8)
|
||||
MOVD(X4, R9)
|
||||
XORL(Mem{Base: SI}.Offset(192), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(196), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(200), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(204), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(192))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(196))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(200))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(204))
|
||||
PADDL(Mem{Base: R12}.Offset(240), X14)
|
||||
PADDL(Mem{Base: R12}.Offset(64), X0)
|
||||
PADDL(Mem{Base: R12}.Offset(128), X5)
|
||||
PADDL(Mem{Base: R12}.Offset(192), X8)
|
||||
MOVD(X14, EDX)
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X5, R8)
|
||||
MOVD(X8, R9)
|
||||
PSHUFL(Imm(0x39), X14, X14)
|
||||
PSHUFL(Imm(0x39), X0, X0)
|
||||
PSHUFL(Imm(0x39), X5, X5)
|
||||
PSHUFL(Imm(0x39), X8, X8)
|
||||
XORL(Mem{Base: SI}.Offset(16), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(20), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(24), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(28), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(16))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(20))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(24))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(28))
|
||||
MOVD(X14, EDX)
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X5, R8)
|
||||
MOVD(X8, R9)
|
||||
PSHUFL(Imm(0x39), X14, X14)
|
||||
PSHUFL(Imm(0x39), X0, X0)
|
||||
PSHUFL(Imm(0x39), X5, X5)
|
||||
PSHUFL(Imm(0x39), X8, X8)
|
||||
XORL(Mem{Base: SI}.Offset(80), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(84), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(88), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(92), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(80))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(84))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(88))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(92))
|
||||
MOVD(X14, EDX)
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X5, R8)
|
||||
MOVD(X8, R9)
|
||||
PSHUFL(Imm(0x39), X14, X14)
|
||||
PSHUFL(Imm(0x39), X0, X0)
|
||||
PSHUFL(Imm(0x39), X5, X5)
|
||||
PSHUFL(Imm(0x39), X8, X8)
|
||||
XORL(Mem{Base: SI}.Offset(144), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(148), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(152), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(156), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(144))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(148))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(152))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(156))
|
||||
MOVD(X14, EDX)
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X5, R8)
|
||||
MOVD(X8, R9)
|
||||
XORL(Mem{Base: SI}.Offset(208), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(212), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(216), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(220), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(208))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(212))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(216))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(220))
|
||||
PADDL(Mem{Base: R12}.Offset(288), X15)
|
||||
PADDL(Mem{Base: R12}.Offset(304), X11)
|
||||
PADDL(Mem{Base: R12}.Offset(80), X1)
|
||||
PADDL(Mem{Base: R12}.Offset(144), X6)
|
||||
MOVD(X15, EDX)
|
||||
MOVD(X11, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X6, R9)
|
||||
PSHUFL(Imm(0x39), X15, X15)
|
||||
PSHUFL(Imm(0x39), X11, X11)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PSHUFL(Imm(0x39), X6, X6)
|
||||
XORL(Mem{Base: SI}.Offset(32), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(36), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(40), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(44), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(32))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(36))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(40))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(44))
|
||||
MOVD(X15, EDX)
|
||||
MOVD(X11, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X6, R9)
|
||||
PSHUFL(Imm(0x39), X15, X15)
|
||||
PSHUFL(Imm(0x39), X11, X11)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PSHUFL(Imm(0x39), X6, X6)
|
||||
XORL(Mem{Base: SI}.Offset(96), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(100), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(104), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(108), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(96))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(100))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(104))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(108))
|
||||
MOVD(X15, EDX)
|
||||
MOVD(X11, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X6, R9)
|
||||
PSHUFL(Imm(0x39), X15, X15)
|
||||
PSHUFL(Imm(0x39), X11, X11)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PSHUFL(Imm(0x39), X6, X6)
|
||||
XORL(Mem{Base: SI}.Offset(160), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(164), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(168), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(172), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(160))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(164))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(168))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(172))
|
||||
MOVD(X15, EDX)
|
||||
MOVD(X11, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X6, R9)
|
||||
XORL(Mem{Base: SI}.Offset(224), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(228), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(232), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(236), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(224))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(228))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(232))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(236))
|
||||
PADDL(Mem{Base: R12}.Offset(160), X13)
|
||||
PADDL(Mem{Base: R12}.Offset(208), X9)
|
||||
PADDL(Mem{Base: R12}.Offset(256), X3)
|
||||
PADDL(Mem{Base: R12}.Offset(96), X2)
|
||||
MOVD(X13, EDX)
|
||||
MOVD(X9, ECX)
|
||||
MOVD(X3, R8)
|
||||
MOVD(X2, R9)
|
||||
PSHUFL(Imm(0x39), X13, X13)
|
||||
PSHUFL(Imm(0x39), X9, X9)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
PSHUFL(Imm(0x39), X2, X2)
|
||||
XORL(Mem{Base: SI}.Offset(48), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(52), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(56), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(60), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(48))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(52))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(56))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(60))
|
||||
MOVD(X13, EDX)
|
||||
MOVD(X9, ECX)
|
||||
MOVD(X3, R8)
|
||||
MOVD(X2, R9)
|
||||
PSHUFL(Imm(0x39), X13, X13)
|
||||
PSHUFL(Imm(0x39), X9, X9)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
PSHUFL(Imm(0x39), X2, X2)
|
||||
XORL(Mem{Base: SI}.Offset(112), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(116), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(120), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(124), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(112))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(116))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(120))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(124))
|
||||
MOVD(X13, EDX)
|
||||
MOVD(X9, ECX)
|
||||
MOVD(X3, R8)
|
||||
MOVD(X2, R9)
|
||||
PSHUFL(Imm(0x39), X13, X13)
|
||||
PSHUFL(Imm(0x39), X9, X9)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
PSHUFL(Imm(0x39), X2, X2)
|
||||
XORL(Mem{Base: SI}.Offset(176), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(180), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(184), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(188), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(176))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(180))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(184))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(188))
|
||||
MOVD(X13, EDX)
|
||||
MOVD(X9, ECX)
|
||||
MOVD(X3, R8)
|
||||
MOVD(X2, R9)
|
||||
XORL(Mem{Base: SI}.Offset(240), EDX)
|
||||
XORL(Mem{Base: SI}.Offset(244), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(248), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(252), R9L)
|
||||
MOVL(EDX, Mem{Base: DI}.Offset(240))
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(244))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(248))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(252))
|
||||
MOVQ(Mem{Base: R12}.Offset(352), R9)
|
||||
SUBQ(U32(256), R9)
|
||||
ADDQ(U32(256), RSI)
|
||||
ADDQ(U32(256), RDI)
|
||||
CMPQ(R9, U32(256))
|
||||
JAE(LabelRef("BYTESATLEAST256"))
|
||||
CMPQ(R9, Imm(0))
|
||||
JBE(LabelRef("DONE"))
|
||||
}
|
||||
|
||||
func BYTESBETWEEN1AND255() {
|
||||
Label("BYTESBETWEEN1AND255")
|
||||
CMPQ(R9, Imm(64))
|
||||
JAE(LabelRef("NOCOPY"))
|
||||
MOVQ(RDI, RDX)
|
||||
LEAQ(Mem{Base: R12}.Offset(360), RDI)
|
||||
MOVQ(R9, RCX)
|
||||
// Hack to get Avo to emit:
|
||||
// REP; MOVSB
|
||||
Instruction(&ir.Instruction{Opcode: "REP; MOVSB"})
|
||||
LEAQ(Mem{Base: R12}.Offset(360), RDI)
|
||||
LEAQ(Mem{Base: R12}.Offset(360), RSI)
|
||||
}
|
||||
|
||||
func NOCOPY() {
|
||||
Label("NOCOPY")
|
||||
MOVQ(R9, Mem{Base: R12}.Offset(352))
|
||||
MOVOA(Mem{Base: R12}.Offset(48), X0)
|
||||
MOVOA(Mem{Base: R12}.Offset(0), X1)
|
||||
MOVOA(Mem{Base: R12}.Offset(16), X2)
|
||||
MOVOA(Mem{Base: R12}.Offset(32), X3)
|
||||
MOVOA(X1, X4)
|
||||
MOVQ(U32(20), RCX)
|
||||
}
|
||||
|
||||
func MAINLOOP2() {
|
||||
Label("MAINLOOP2")
|
||||
PADDL(X0, X4)
|
||||
MOVOA(X0, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(7), X4)
|
||||
PSRLL(Imm(25), X6)
|
||||
PXOR(X4, X3)
|
||||
PXOR(X6, X3)
|
||||
PADDL(X3, X5)
|
||||
MOVOA(X3, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(9), X5)
|
||||
PSRLL(Imm(23), X6)
|
||||
PXOR(X5, X2)
|
||||
PSHUFL(Imm(0x93), X3, X3)
|
||||
PXOR(X6, X2)
|
||||
PADDL(X2, X4)
|
||||
MOVOA(X2, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(13), X4)
|
||||
PSRLL(Imm(19), X6)
|
||||
PXOR(X4, X1)
|
||||
PSHUFL(Imm(0x4E), X2, X2)
|
||||
PXOR(X6, X1)
|
||||
PADDL(X1, X5)
|
||||
MOVOA(X3, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(18), X5)
|
||||
PSRLL(Imm(14), X6)
|
||||
PXOR(X5, X0)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PXOR(X6, X0)
|
||||
PADDL(X0, X4)
|
||||
MOVOA(X0, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(7), X4)
|
||||
PSRLL(Imm(25), X6)
|
||||
PXOR(X4, X1)
|
||||
PXOR(X6, X1)
|
||||
PADDL(X1, X5)
|
||||
MOVOA(X1, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(9), X5)
|
||||
PSRLL(Imm(23), X6)
|
||||
PXOR(X5, X2)
|
||||
PSHUFL(Imm(0x93), X1, X1)
|
||||
PXOR(X6, X2)
|
||||
PADDL(X2, X4)
|
||||
MOVOA(X2, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(13), X4)
|
||||
PSRLL(Imm(19), X6)
|
||||
PXOR(X4, X3)
|
||||
PSHUFL(Imm(0x4E), X2, X2)
|
||||
PXOR(X6, X3)
|
||||
PADDL(X3, X5)
|
||||
MOVOA(X1, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(18), X5)
|
||||
PSRLL(Imm(14), X6)
|
||||
PXOR(X5, X0)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
PXOR(X6, X0)
|
||||
PADDL(X0, X4)
|
||||
MOVOA(X0, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(7), X4)
|
||||
PSRLL(Imm(25), X6)
|
||||
PXOR(X4, X3)
|
||||
PXOR(X6, X3)
|
||||
PADDL(X3, X5)
|
||||
MOVOA(X3, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(9), X5)
|
||||
PSRLL(Imm(23), X6)
|
||||
PXOR(X5, X2)
|
||||
PSHUFL(Imm(0x93), X3, X3)
|
||||
PXOR(X6, X2)
|
||||
PADDL(X2, X4)
|
||||
MOVOA(X2, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(13), X4)
|
||||
PSRLL(Imm(19), X6)
|
||||
PXOR(X4, X1)
|
||||
PSHUFL(Imm(0x4E), X2, X2)
|
||||
PXOR(X6, X1)
|
||||
PADDL(X1, X5)
|
||||
MOVOA(X3, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(18), X5)
|
||||
PSRLL(Imm(14), X6)
|
||||
PXOR(X5, X0)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PXOR(X6, X0)
|
||||
PADDL(X0, X4)
|
||||
MOVOA(X0, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(7), X4)
|
||||
PSRLL(Imm(25), X6)
|
||||
PXOR(X4, X1)
|
||||
PXOR(X6, X1)
|
||||
PADDL(X1, X5)
|
||||
MOVOA(X1, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(9), X5)
|
||||
PSRLL(Imm(23), X6)
|
||||
PXOR(X5, X2)
|
||||
PSHUFL(Imm(0x93), X1, X1)
|
||||
PXOR(X6, X2)
|
||||
PADDL(X2, X4)
|
||||
MOVOA(X2, X5)
|
||||
MOVOA(X4, X6)
|
||||
PSLLL(Imm(13), X4)
|
||||
PSRLL(Imm(19), X6)
|
||||
PXOR(X4, X3)
|
||||
PSHUFL(Imm(0x4E), X2, X2)
|
||||
PXOR(X6, X3)
|
||||
SUBQ(Imm(4), RCX)
|
||||
PADDL(X3, X5)
|
||||
MOVOA(X1, X4)
|
||||
MOVOA(X5, X6)
|
||||
PSLLL(Imm(18), X5)
|
||||
PXOR(X7, X7)
|
||||
PSRLL(Imm(14), X6)
|
||||
PXOR(X5, X0)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
PXOR(X6, X0)
|
||||
JA(LabelRef("MAINLOOP2"))
|
||||
PADDL(Mem{Base: R12}.Offset(48), X0)
|
||||
PADDL(Mem{Base: R12}.Offset(0), X1)
|
||||
PADDL(Mem{Base: R12}.Offset(16), X2)
|
||||
PADDL(Mem{Base: R12}.Offset(32), X3)
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X2, R9)
|
||||
MOVD(X3, EAX)
|
||||
PSHUFL(Imm(0x39), X0, X0)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PSHUFL(Imm(0x39), X2, X2)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
XORL(Mem{Base: SI}.Offset(0), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(48), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(32), R9L)
|
||||
XORL(Mem{Base: SI}.Offset(16), EAX)
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(0))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(48))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(32))
|
||||
MOVL(EAX, Mem{Base: DI}.Offset(16))
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X2, R9)
|
||||
MOVD(X3, EAX)
|
||||
PSHUFL(Imm(0x39), X0, X0)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PSHUFL(Imm(0x39), X2, X2)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
XORL(Mem{Base: SI}.Offset(20), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(4), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(52), R9L)
|
||||
XORL(Mem{Base: SI}.Offset(36), EAX)
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(20))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(4))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(52))
|
||||
MOVL(EAX, Mem{Base: DI}.Offset(36))
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X2, R9)
|
||||
MOVD(X3, EAX)
|
||||
PSHUFL(Imm(0x39), X0, X0)
|
||||
PSHUFL(Imm(0x39), X1, X1)
|
||||
PSHUFL(Imm(0x39), X2, X2)
|
||||
PSHUFL(Imm(0x39), X3, X3)
|
||||
XORL(Mem{Base: SI}.Offset(40), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(24), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(8), R9L)
|
||||
XORL(Mem{Base: SI}.Offset(56), EAX)
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(40))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(24))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(8))
|
||||
MOVL(EAX, Mem{Base: DI}.Offset(56))
|
||||
MOVD(X0, ECX)
|
||||
MOVD(X1, R8)
|
||||
MOVD(X2, R9)
|
||||
MOVD(X3, EAX)
|
||||
XORL(Mem{Base: SI}.Offset(60), ECX)
|
||||
XORL(Mem{Base: SI}.Offset(44), R8L)
|
||||
XORL(Mem{Base: SI}.Offset(28), R9L)
|
||||
XORL(Mem{Base: SI}.Offset(12), EAX)
|
||||
MOVL(ECX, Mem{Base: DI}.Offset(60))
|
||||
MOVL(R8L, Mem{Base: DI}.Offset(44))
|
||||
MOVL(R9L, Mem{Base: DI}.Offset(28))
|
||||
MOVL(EAX, Mem{Base: DI}.Offset(12))
|
||||
MOVQ(Mem{Base: R12}.Offset(352), R9)
|
||||
MOVL(Mem{Base: R12}.Offset(16), ECX)
|
||||
MOVL(Mem{Base: R12}.Offset(36), R8L)
|
||||
ADDQ(Imm(1), RCX)
|
||||
SHLQ(Imm(32), R8)
|
||||
ADDQ(R8, RCX)
|
||||
MOVQ(RCX, R8)
|
||||
SHRQ(Imm(32), R8)
|
||||
MOVL(ECX, Mem{Base: R12}.Offset(16))
|
||||
MOVL(R8L, Mem{Base: R12}.Offset(36))
|
||||
CMPQ(R9, Imm(64))
|
||||
JA(LabelRef("BYTESATLEAST65"))
|
||||
JAE(LabelRef("BYTESATLEAST64"))
|
||||
MOVQ(RDI, RSI)
|
||||
MOVQ(RDX, RDI)
|
||||
MOVQ(R9, RCX)
|
||||
// Hack to get Avo to emit:
|
||||
// REP; MOVSB
|
||||
Instruction(&ir.Instruction{Opcode: "REP; MOVSB"})
|
||||
}
|
||||
+871
-871
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -4,12 +4,12 @@ go 1.22
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.6.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
)
|
||||
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
// Package sha3 implements the SHA-3 fixed-output-length hash functions and
|
||||
// the SHAKE variable-output-length hash functions defined by FIPS-202.
|
||||
//
|
||||
// All types in this package also implement [encoding.BinaryMarshaler],
|
||||
// [encoding.BinaryAppender] and [encoding.BinaryUnmarshaler] to marshal and
|
||||
// unmarshal the internal state of the hash.
|
||||
//
|
||||
// Both types of hash function use the "sponge" construction and the Keccak
|
||||
// permutation. For a detailed specification see http://keccak.noekeon.org/
|
||||
//
|
||||
|
||||
+25
-6
@@ -48,33 +48,52 @@ func init() {
|
||||
crypto.RegisterHash(crypto.SHA3_512, New512)
|
||||
}
|
||||
|
||||
const (
|
||||
dsbyteSHA3 = 0b00000110
|
||||
dsbyteKeccak = 0b00000001
|
||||
dsbyteShake = 0b00011111
|
||||
dsbyteCShake = 0b00000100
|
||||
|
||||
// rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in
|
||||
// bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits.
|
||||
rateK256 = (1600 - 256) / 8
|
||||
rateK448 = (1600 - 448) / 8
|
||||
rateK512 = (1600 - 512) / 8
|
||||
rateK768 = (1600 - 768) / 8
|
||||
rateK1024 = (1600 - 1024) / 8
|
||||
)
|
||||
|
||||
func new224Generic() *state {
|
||||
return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
|
||||
return &state{rate: rateK448, outputLen: 28, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
func new256Generic() *state {
|
||||
return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
|
||||
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
func new384Generic() *state {
|
||||
return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
|
||||
return &state{rate: rateK768, outputLen: 48, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
func new512Generic() *state {
|
||||
return &state{rate: 72, outputLen: 64, dsbyte: 0x06}
|
||||
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
// NewLegacyKeccak256 creates a new Keccak-256 hash.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing cryptosystem
|
||||
// that uses non-standard padding. All other users should use New256 instead.
|
||||
func NewLegacyKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} }
|
||||
func NewLegacyKeccak256() hash.Hash {
|
||||
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak}
|
||||
}
|
||||
|
||||
// NewLegacyKeccak512 creates a new Keccak-512 hash.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing cryptosystem
|
||||
// that uses non-standard padding. All other users should use New512 instead.
|
||||
func NewLegacyKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} }
|
||||
func NewLegacyKeccak512() hash.Hash {
|
||||
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak}
|
||||
}
|
||||
|
||||
// Sum224 returns the SHA3-224 digest of the data.
|
||||
func Sum224(data []byte) (digest [28]byte) {
|
||||
|
||||
+122
-63
@@ -4,6 +4,15 @@
|
||||
|
||||
package sha3
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// spongeDirection indicates the direction bytes are flowing through the sponge.
|
||||
type spongeDirection int
|
||||
|
||||
@@ -14,16 +23,13 @@ const (
|
||||
spongeSqueezing
|
||||
)
|
||||
|
||||
const (
|
||||
// maxRate is the maximum size of the internal buffer. SHAKE-256
|
||||
// currently needs the largest buffer.
|
||||
maxRate = 168
|
||||
)
|
||||
|
||||
type state struct {
|
||||
// Generic sponge components.
|
||||
a [25]uint64 // main state of the hash
|
||||
rate int // the number of bytes of state to use
|
||||
a [1600 / 8]byte // main state of the hash
|
||||
|
||||
// a[n:rate] is the buffer. If absorbing, it's the remaining space to XOR
|
||||
// into before running the permutation. If squeezing, it's the remaining
|
||||
// output to produce before running the permutation.
|
||||
n, rate int
|
||||
|
||||
// dsbyte contains the "domain separation" bits and the first bit of
|
||||
// the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
|
||||
@@ -39,10 +45,6 @@ type state struct {
|
||||
// Extendable-Output Functions (May 2014)"
|
||||
dsbyte byte
|
||||
|
||||
i, n int // storage[i:n] is the buffer, i is only used while squeezing
|
||||
storage [maxRate]byte
|
||||
|
||||
// Specific to SHA-3 and SHAKE.
|
||||
outputLen int // the default output size in bytes
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
}
|
||||
@@ -61,7 +63,7 @@ func (d *state) Reset() {
|
||||
d.a[i] = 0
|
||||
}
|
||||
d.state = spongeAbsorbing
|
||||
d.i, d.n = 0, 0
|
||||
d.n = 0
|
||||
}
|
||||
|
||||
func (d *state) clone() *state {
|
||||
@@ -69,22 +71,25 @@ func (d *state) clone() *state {
|
||||
return &ret
|
||||
}
|
||||
|
||||
// permute applies the KeccakF-1600 permutation. It handles
|
||||
// any input-output buffering.
|
||||
// permute applies the KeccakF-1600 permutation.
|
||||
func (d *state) permute() {
|
||||
switch d.state {
|
||||
case spongeAbsorbing:
|
||||
// If we're absorbing, we need to xor the input into the state
|
||||
// before applying the permutation.
|
||||
xorIn(d, d.storage[:d.rate])
|
||||
d.n = 0
|
||||
keccakF1600(&d.a)
|
||||
case spongeSqueezing:
|
||||
// If we're squeezing, we need to apply the permutation before
|
||||
// copying more output.
|
||||
keccakF1600(&d.a)
|
||||
d.i = 0
|
||||
copyOut(d, d.storage[:d.rate])
|
||||
var a *[25]uint64
|
||||
if cpu.IsBigEndian {
|
||||
a = new([25]uint64)
|
||||
for i := range a {
|
||||
a[i] = binary.LittleEndian.Uint64(d.a[i*8:])
|
||||
}
|
||||
} else {
|
||||
a = (*[25]uint64)(unsafe.Pointer(&d.a))
|
||||
}
|
||||
|
||||
keccakF1600(a)
|
||||
d.n = 0
|
||||
|
||||
if cpu.IsBigEndian {
|
||||
for i := range a {
|
||||
binary.LittleEndian.PutUint64(d.a[i*8:], a[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,53 +97,36 @@ func (d *state) permute() {
|
||||
// the multi-bitrate 10..1 padding rule, and permutes the state.
|
||||
func (d *state) padAndPermute() {
|
||||
// Pad with this instance's domain-separator bits. We know that there's
|
||||
// at least one byte of space in d.buf because, if it were full,
|
||||
// at least one byte of space in the sponge because, if it were full,
|
||||
// permute would have been called to empty it. dsbyte also contains the
|
||||
// first one bit for the padding. See the comment in the state struct.
|
||||
d.storage[d.n] = d.dsbyte
|
||||
d.n++
|
||||
for d.n < d.rate {
|
||||
d.storage[d.n] = 0
|
||||
d.n++
|
||||
}
|
||||
d.a[d.n] ^= d.dsbyte
|
||||
// This adds the final one bit for the padding. Because of the way that
|
||||
// bits are numbered from the LSB upwards, the final bit is the MSB of
|
||||
// the last byte.
|
||||
d.storage[d.rate-1] ^= 0x80
|
||||
d.a[d.rate-1] ^= 0x80
|
||||
// Apply the permutation
|
||||
d.permute()
|
||||
d.state = spongeSqueezing
|
||||
d.n = d.rate
|
||||
copyOut(d, d.storage[:d.rate])
|
||||
}
|
||||
|
||||
// Write absorbs more data into the hash's state. It panics if any
|
||||
// output has already been read.
|
||||
func (d *state) Write(p []byte) (written int, err error) {
|
||||
func (d *state) Write(p []byte) (n int, err error) {
|
||||
if d.state != spongeAbsorbing {
|
||||
panic("sha3: Write after Read")
|
||||
}
|
||||
written = len(p)
|
||||
|
||||
n = len(p)
|
||||
|
||||
for len(p) > 0 {
|
||||
if d.n == 0 && len(p) >= d.rate {
|
||||
// The fast path; absorb a full "rate" bytes of input and apply the permutation.
|
||||
xorIn(d, p[:d.rate])
|
||||
p = p[d.rate:]
|
||||
keccakF1600(&d.a)
|
||||
} else {
|
||||
// The slow path; buffer the input until we can fill the sponge, and then xor it in.
|
||||
todo := d.rate - d.n
|
||||
if todo > len(p) {
|
||||
todo = len(p)
|
||||
}
|
||||
d.n += copy(d.storage[d.n:], p[:todo])
|
||||
p = p[todo:]
|
||||
x := subtle.XORBytes(d.a[d.n:d.rate], d.a[d.n:d.rate], p)
|
||||
d.n += x
|
||||
p = p[x:]
|
||||
|
||||
// If the sponge is full, apply the permutation.
|
||||
if d.n == d.rate {
|
||||
d.permute()
|
||||
}
|
||||
// If the sponge is full, apply the permutation.
|
||||
if d.n == d.rate {
|
||||
d.permute()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,14 +144,14 @@ func (d *state) Read(out []byte) (n int, err error) {
|
||||
|
||||
// Now, do the squeezing.
|
||||
for len(out) > 0 {
|
||||
n := copy(out, d.storage[d.i:d.n])
|
||||
d.i += n
|
||||
out = out[n:]
|
||||
|
||||
// Apply the permutation if we've squeezed the sponge dry.
|
||||
if d.i == d.rate {
|
||||
if d.n == d.rate {
|
||||
d.permute()
|
||||
}
|
||||
|
||||
x := copy(out, d.a[d.n:d.rate])
|
||||
d.n += x
|
||||
out = out[x:]
|
||||
}
|
||||
|
||||
return
|
||||
@@ -183,3 +171,74 @@ func (d *state) Sum(in []byte) []byte {
|
||||
dup.Read(hash)
|
||||
return append(in, hash...)
|
||||
}
|
||||
|
||||
const (
|
||||
magicSHA3 = "sha\x08"
|
||||
magicShake = "sha\x09"
|
||||
magicCShake = "sha\x0a"
|
||||
magicKeccak = "sha\x0b"
|
||||
// magic || rate || main state || n || sponge direction
|
||||
marshaledSize = len(magicSHA3) + 1 + 200 + 1 + 1
|
||||
)
|
||||
|
||||
func (d *state) MarshalBinary() ([]byte, error) {
|
||||
return d.AppendBinary(make([]byte, 0, marshaledSize))
|
||||
}
|
||||
|
||||
func (d *state) AppendBinary(b []byte) ([]byte, error) {
|
||||
switch d.dsbyte {
|
||||
case dsbyteSHA3:
|
||||
b = append(b, magicSHA3...)
|
||||
case dsbyteShake:
|
||||
b = append(b, magicShake...)
|
||||
case dsbyteCShake:
|
||||
b = append(b, magicCShake...)
|
||||
case dsbyteKeccak:
|
||||
b = append(b, magicKeccak...)
|
||||
default:
|
||||
panic("unknown dsbyte")
|
||||
}
|
||||
// rate is at most 168, and n is at most rate.
|
||||
b = append(b, byte(d.rate))
|
||||
b = append(b, d.a[:]...)
|
||||
b = append(b, byte(d.n), byte(d.state))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (d *state) UnmarshalBinary(b []byte) error {
|
||||
if len(b) != marshaledSize {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
|
||||
magic := string(b[:len(magicSHA3)])
|
||||
b = b[len(magicSHA3):]
|
||||
switch {
|
||||
case magic == magicSHA3 && d.dsbyte == dsbyteSHA3:
|
||||
case magic == magicShake && d.dsbyte == dsbyteShake:
|
||||
case magic == magicCShake && d.dsbyte == dsbyteCShake:
|
||||
case magic == magicKeccak && d.dsbyte == dsbyteKeccak:
|
||||
default:
|
||||
return errors.New("sha3: invalid hash state identifier")
|
||||
}
|
||||
|
||||
rate := int(b[0])
|
||||
b = b[1:]
|
||||
if rate != d.rate {
|
||||
return errors.New("sha3: invalid hash state function")
|
||||
}
|
||||
|
||||
copy(d.a[:], b)
|
||||
b = b[len(d.a):]
|
||||
|
||||
n, state := int(b[0]), spongeDirection(b[1])
|
||||
if n > d.rate {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
d.n = n
|
||||
if state != spongeAbsorbing && state != spongeSqueezing {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
d.state = state
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@ package sha3
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"encoding"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -375,6 +377,153 @@ func TestClone(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSHAKEAccumulated(t *testing.T) {
|
||||
// Generated with pycryptodome@3.20.0
|
||||
//
|
||||
// from Crypto.Hash import cSHAKE128
|
||||
// rng = cSHAKE128.new()
|
||||
// acc = cSHAKE128.new()
|
||||
// for n in range(200):
|
||||
// N = rng.read(n)
|
||||
// for s in range(200):
|
||||
// S = rng.read(s)
|
||||
// c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
|
||||
// c.update(rng.read(100))
|
||||
// acc.update(c.read(200))
|
||||
// c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
|
||||
// c.update(rng.read(168))
|
||||
// acc.update(c.read(200))
|
||||
// c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
|
||||
// c.update(rng.read(200))
|
||||
// acc.update(c.read(200))
|
||||
// print(acc.read(32).hex())
|
||||
//
|
||||
// and with @noble/hashes@v1.5.0
|
||||
//
|
||||
// import { bytesToHex } from "@noble/hashes/utils";
|
||||
// import { cshake128 } from "@noble/hashes/sha3-addons";
|
||||
// const rng = cshake128.create();
|
||||
// const acc = cshake128.create();
|
||||
// for (let n = 0; n < 200; n++) {
|
||||
// const N = rng.xof(n);
|
||||
// for (let s = 0; s < 200; s++) {
|
||||
// const S = rng.xof(s);
|
||||
// let c = cshake128.create({ NISTfn: N, personalization: S });
|
||||
// c.update(rng.xof(100));
|
||||
// acc.update(c.xof(200));
|
||||
// c = cshake128.create({ NISTfn: N, personalization: S });
|
||||
// c.update(rng.xof(168));
|
||||
// acc.update(c.xof(200));
|
||||
// c = cshake128.create({ NISTfn: N, personalization: S });
|
||||
// c.update(rng.xof(200));
|
||||
// acc.update(c.xof(200));
|
||||
// }
|
||||
// }
|
||||
// console.log(bytesToHex(acc.xof(32)));
|
||||
//
|
||||
t.Run("cSHAKE128", func(t *testing.T) {
|
||||
testCSHAKEAccumulated(t, NewCShake128, rateK256,
|
||||
"bb14f8657c6ec5403d0b0e2ef3d3393497e9d3b1a9a9e8e6c81dbaa5fd809252")
|
||||
})
|
||||
t.Run("cSHAKE256", func(t *testing.T) {
|
||||
testCSHAKEAccumulated(t, NewCShake256, rateK512,
|
||||
"0baaf9250c6e25f0c14ea5c7f9bfde54c8a922c8276437db28f3895bdf6eeeef")
|
||||
})
|
||||
}
|
||||
|
||||
func testCSHAKEAccumulated(t *testing.T, newCShake func(N, S []byte) ShakeHash, rate int64, exp string) {
|
||||
rnd := newCShake(nil, nil)
|
||||
acc := newCShake(nil, nil)
|
||||
for n := 0; n < 200; n++ {
|
||||
N := make([]byte, n)
|
||||
rnd.Read(N)
|
||||
for s := 0; s < 200; s++ {
|
||||
S := make([]byte, s)
|
||||
rnd.Read(S)
|
||||
|
||||
c := newCShake(N, S)
|
||||
io.CopyN(c, rnd, 100 /* < rate */)
|
||||
io.CopyN(acc, c, 200)
|
||||
|
||||
c.Reset()
|
||||
io.CopyN(c, rnd, rate)
|
||||
io.CopyN(acc, c, 200)
|
||||
|
||||
c.Reset()
|
||||
io.CopyN(c, rnd, 200 /* > rate */)
|
||||
io.CopyN(acc, c, 200)
|
||||
}
|
||||
}
|
||||
if got := hex.EncodeToString(acc.Sum(nil)[:32]); got != exp {
|
||||
t.Errorf("got %s, want %s", got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSHAKELargeS(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
}
|
||||
|
||||
// See https://go.dev/issue/66232.
|
||||
const s = (1<<32)/8 + 1000 // s * 8 > 2^32
|
||||
S := make([]byte, s)
|
||||
rnd := NewShake128()
|
||||
rnd.Read(S)
|
||||
c := NewCShake128(nil, S)
|
||||
io.CopyN(c, rnd, 1000)
|
||||
|
||||
// Generated with pycryptodome@3.20.0
|
||||
//
|
||||
// from Crypto.Hash import cSHAKE128
|
||||
// rng = cSHAKE128.new()
|
||||
// S = rng.read(536871912)
|
||||
// c = cSHAKE128.new(custom=S)
|
||||
// c.update(rng.read(1000))
|
||||
// print(c.read(32).hex())
|
||||
//
|
||||
exp := "2cb9f237767e98f2614b8779cf096a52da9b3a849280bbddec820771ae529cf0"
|
||||
if got := hex.EncodeToString(c.Sum(nil)); got != exp {
|
||||
t.Errorf("got %s, want %s", got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshal(t *testing.T) {
|
||||
t.Run("SHA3-224", func(t *testing.T) { testMarshalUnmarshal(t, New224()) })
|
||||
t.Run("SHA3-256", func(t *testing.T) { testMarshalUnmarshal(t, New256()) })
|
||||
t.Run("SHA3-384", func(t *testing.T) { testMarshalUnmarshal(t, New384()) })
|
||||
t.Run("SHA3-512", func(t *testing.T) { testMarshalUnmarshal(t, New512()) })
|
||||
t.Run("SHAKE128", func(t *testing.T) { testMarshalUnmarshal(t, NewShake128()) })
|
||||
t.Run("SHAKE256", func(t *testing.T) { testMarshalUnmarshal(t, NewShake256()) })
|
||||
t.Run("cSHAKE128", func(t *testing.T) { testMarshalUnmarshal(t, NewCShake128([]byte("N"), []byte("S"))) })
|
||||
t.Run("cSHAKE256", func(t *testing.T) { testMarshalUnmarshal(t, NewCShake256([]byte("N"), []byte("S"))) })
|
||||
t.Run("Keccak-256", func(t *testing.T) { testMarshalUnmarshal(t, NewLegacyKeccak256()) })
|
||||
t.Run("Keccak-512", func(t *testing.T) { testMarshalUnmarshal(t, NewLegacyKeccak512()) })
|
||||
}
|
||||
|
||||
// TODO(filippo): move this to crypto/internal/cryptotest.
|
||||
func testMarshalUnmarshal(t *testing.T, h hash.Hash) {
|
||||
buf := make([]byte, 200)
|
||||
rand.Read(buf)
|
||||
n := rand.Intn(200)
|
||||
h.Write(buf)
|
||||
want := h.Sum(nil)
|
||||
h.Reset()
|
||||
h.Write(buf[:n])
|
||||
b, err := h.(encoding.BinaryMarshaler).MarshalBinary()
|
||||
if err != nil {
|
||||
t.Errorf("MarshalBinary: %v", err)
|
||||
}
|
||||
h.Write(bytes.Repeat([]byte{0}, 200))
|
||||
if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary(b); err != nil {
|
||||
t.Errorf("UnmarshalBinary: %v", err)
|
||||
}
|
||||
h.Write(buf[n:])
|
||||
got := h.Sum(nil)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("got %x, want %x", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkPermutationFunction measures the speed of the permutation function
|
||||
// with no input data.
|
||||
func BenchmarkPermutationFunction(b *testing.B) {
|
||||
|
||||
+53
-34
@@ -16,9 +16,12 @@ package sha3
|
||||
// [2] https://doi.org/10.6028/NIST.SP.800-185
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// ShakeHash defines the interface to hash functions that support
|
||||
@@ -50,44 +53,36 @@ type cshakeState struct {
|
||||
initBlock []byte
|
||||
}
|
||||
|
||||
// Consts for configuring initial SHA-3 state
|
||||
const (
|
||||
dsbyteShake = 0x1f
|
||||
dsbyteCShake = 0x04
|
||||
rate128 = 168
|
||||
rate256 = 136
|
||||
)
|
||||
|
||||
func bytepad(input []byte, w int) []byte {
|
||||
// leftEncode always returns max 9 bytes
|
||||
buf := make([]byte, 0, 9+len(input)+w)
|
||||
buf = append(buf, leftEncode(uint64(w))...)
|
||||
buf = append(buf, input...)
|
||||
padlen := w - (len(buf) % w)
|
||||
return append(buf, make([]byte, padlen)...)
|
||||
func bytepad(data []byte, rate int) []byte {
|
||||
out := make([]byte, 0, 9+len(data)+rate-1)
|
||||
out = append(out, leftEncode(uint64(rate))...)
|
||||
out = append(out, data...)
|
||||
if padlen := rate - len(out)%rate; padlen < rate {
|
||||
out = append(out, make([]byte, padlen)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func leftEncode(value uint64) []byte {
|
||||
var b [9]byte
|
||||
binary.BigEndian.PutUint64(b[1:], value)
|
||||
// Trim all but last leading zero bytes
|
||||
i := byte(1)
|
||||
for i < 8 && b[i] == 0 {
|
||||
i++
|
||||
func leftEncode(x uint64) []byte {
|
||||
// Let n be the smallest positive integer for which 2^(8n) > x.
|
||||
n := (bits.Len64(x) + 7) / 8
|
||||
if n == 0 {
|
||||
n = 1
|
||||
}
|
||||
// Prepend number of encoded bytes
|
||||
b[i-1] = 9 - i
|
||||
return b[i-1:]
|
||||
// Return n || x with n as a byte and x an n bytes in big-endian order.
|
||||
b := make([]byte, 9)
|
||||
binary.BigEndian.PutUint64(b[1:], x)
|
||||
b = b[9-n-1:]
|
||||
b[0] = byte(n)
|
||||
return b
|
||||
}
|
||||
|
||||
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
|
||||
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
|
||||
|
||||
// leftEncode returns max 9 bytes
|
||||
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N)*8))...)
|
||||
c.initBlock = make([]byte, 0, 9+len(N)+9+len(S)) // leftEncode returns max 9 bytes
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N))*8)...)
|
||||
c.initBlock = append(c.initBlock, N...)
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S)*8))...)
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S))*8)...)
|
||||
c.initBlock = append(c.initBlock, S...)
|
||||
c.Write(bytepad(c.initBlock, c.rate))
|
||||
return &c
|
||||
@@ -111,6 +106,30 @@ func (c *state) Clone() ShakeHash {
|
||||
return c.clone()
|
||||
}
|
||||
|
||||
func (c *cshakeState) MarshalBinary() ([]byte, error) {
|
||||
return c.AppendBinary(make([]byte, 0, marshaledSize+len(c.initBlock)))
|
||||
}
|
||||
|
||||
func (c *cshakeState) AppendBinary(b []byte) ([]byte, error) {
|
||||
b, err := c.state.AppendBinary(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = append(b, c.initBlock...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (c *cshakeState) UnmarshalBinary(b []byte) error {
|
||||
if len(b) <= marshaledSize {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
if err := c.state.UnmarshalBinary(b[:marshaledSize]); err != nil {
|
||||
return err
|
||||
}
|
||||
c.initBlock = bytes.Clone(b[marshaledSize:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
|
||||
// Its generic security strength is 128 bits against all attacks if at
|
||||
// least 32 bytes of its output are used.
|
||||
@@ -126,11 +145,11 @@ func NewShake256() ShakeHash {
|
||||
}
|
||||
|
||||
func newShake128Generic() *state {
|
||||
return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
|
||||
return &state{rate: rateK256, outputLen: 32, dsbyte: dsbyteShake}
|
||||
}
|
||||
|
||||
func newShake256Generic() *state {
|
||||
return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake}
|
||||
return &state{rate: rateK512, outputLen: 64, dsbyte: dsbyteShake}
|
||||
}
|
||||
|
||||
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
|
||||
@@ -143,7 +162,7 @@ func NewCShake128(N, S []byte) ShakeHash {
|
||||
if len(N) == 0 && len(S) == 0 {
|
||||
return NewShake128()
|
||||
}
|
||||
return newCShake(N, S, rate128, 32, dsbyteCShake)
|
||||
return newCShake(N, S, rateK256, 32, dsbyteCShake)
|
||||
}
|
||||
|
||||
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
|
||||
@@ -156,7 +175,7 @@ func NewCShake256(N, S []byte) ShakeHash {
|
||||
if len(N) == 0 && len(S) == 0 {
|
||||
return NewShake256()
|
||||
}
|
||||
return newCShake(N, S, rate256, 64, dsbyteCShake)
|
||||
return newCShake(N, S, rateK512, 64, dsbyteCShake)
|
||||
}
|
||||
|
||||
// ShakeSum128 writes an arbitrary-length digest of data into hash.
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
// Copyright 2015 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 sha3
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// xorIn xors the bytes in buf into the state.
|
||||
func xorIn(d *state, buf []byte) {
|
||||
if cpu.IsBigEndian {
|
||||
for i := 0; len(buf) >= 8; i++ {
|
||||
a := binary.LittleEndian.Uint64(buf)
|
||||
d.a[i] ^= a
|
||||
buf = buf[8:]
|
||||
}
|
||||
} else {
|
||||
ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a))
|
||||
subtle.XORBytes(ab[:], ab[:], buf)
|
||||
}
|
||||
}
|
||||
|
||||
// copyOut copies uint64s to a byte buffer.
|
||||
func copyOut(d *state, b []byte) {
|
||||
if cpu.IsBigEndian {
|
||||
for i := 0; len(b) >= 8; i++ {
|
||||
binary.LittleEndian.PutUint64(b, d.a[i])
|
||||
b = b[8:]
|
||||
}
|
||||
} else {
|
||||
ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a))
|
||||
copy(b, ab[:])
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -555,7 +555,7 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
|
||||
})
|
||||
case *dsa.PrivateKey:
|
||||
req = ssh.Marshal(dsaKeyMsg{
|
||||
Type: ssh.KeyAlgoDSA,
|
||||
Type: ssh.InsecureKeyAlgoDSA,
|
||||
P: k.P,
|
||||
Q: k.Q,
|
||||
G: k.G,
|
||||
@@ -803,16 +803,16 @@ var _ ssh.AlgorithmSigner = &agentKeyringSigner{}
|
||||
//
|
||||
// This map must be kept in sync with the one in certs.go.
|
||||
var certKeyAlgoNames = map[string]string{
|
||||
ssh.CertAlgoRSAv01: ssh.KeyAlgoRSA,
|
||||
ssh.CertAlgoRSASHA256v01: ssh.KeyAlgoRSASHA256,
|
||||
ssh.CertAlgoRSASHA512v01: ssh.KeyAlgoRSASHA512,
|
||||
ssh.CertAlgoDSAv01: ssh.KeyAlgoDSA,
|
||||
ssh.CertAlgoECDSA256v01: ssh.KeyAlgoECDSA256,
|
||||
ssh.CertAlgoECDSA384v01: ssh.KeyAlgoECDSA384,
|
||||
ssh.CertAlgoECDSA521v01: ssh.KeyAlgoECDSA521,
|
||||
ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
|
||||
ssh.CertAlgoED25519v01: ssh.KeyAlgoED25519,
|
||||
ssh.CertAlgoSKED25519v01: ssh.KeyAlgoSKED25519,
|
||||
ssh.CertAlgoRSAv01: ssh.KeyAlgoRSA,
|
||||
ssh.CertAlgoRSASHA256v01: ssh.KeyAlgoRSASHA256,
|
||||
ssh.CertAlgoRSASHA512v01: ssh.KeyAlgoRSASHA512,
|
||||
ssh.InsecureCertAlgoDSAv01: ssh.InsecureKeyAlgoDSA,
|
||||
ssh.CertAlgoECDSA256v01: ssh.KeyAlgoECDSA256,
|
||||
ssh.CertAlgoECDSA384v01: ssh.KeyAlgoECDSA384,
|
||||
ssh.CertAlgoECDSA521v01: ssh.KeyAlgoECDSA521,
|
||||
ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
|
||||
ssh.CertAlgoED25519v01: ssh.KeyAlgoED25519,
|
||||
ssh.CertAlgoSKED25519v01: ssh.KeyAlgoSKED25519,
|
||||
}
|
||||
|
||||
// underlyingAlgo returns the signature algorithm associated with algo (which is
|
||||
|
||||
@@ -175,6 +175,15 @@ func (r *keyring) Add(key AddedKey) error {
|
||||
p.expire = &t
|
||||
}
|
||||
|
||||
// If we already have a Signer with the same public key, replace it with the
|
||||
// new one.
|
||||
for idx, k := range r.keys {
|
||||
if bytes.Equal(k.signer.PublicKey().Marshal(), p.signer.PublicKey().Marshal()) {
|
||||
r.keys[idx] = p
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
r.keys = append(r.keys, p)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -29,6 +29,10 @@ func validateListedKeys(t *testing.T, a Agent, expectedKeys []string) {
|
||||
t.Fatalf("failed to list keys: %v", err)
|
||||
return
|
||||
}
|
||||
if len(listedKeys) != len(expectedKeys) {
|
||||
t.Fatalf("expeted %d key, got %d", len(expectedKeys), len(listedKeys))
|
||||
return
|
||||
}
|
||||
actualKeys := make(map[string]bool)
|
||||
for _, key := range listedKeys {
|
||||
actualKeys[key.Comment] = true
|
||||
@@ -74,3 +78,45 @@ func TestKeyringAddingAndRemoving(t *testing.T) {
|
||||
}
|
||||
validateListedKeys(t, k, []string{})
|
||||
}
|
||||
|
||||
func TestAddDuplicateKey(t *testing.T) {
|
||||
keyNames := []string{"rsa", "user"}
|
||||
|
||||
k := NewKeyring()
|
||||
for _, keyName := range keyNames {
|
||||
addTestKey(t, k, keyName)
|
||||
}
|
||||
validateListedKeys(t, k, keyNames)
|
||||
// Add the keys again.
|
||||
for _, keyName := range keyNames {
|
||||
addTestKey(t, k, keyName)
|
||||
}
|
||||
validateListedKeys(t, k, keyNames)
|
||||
// Add an existing key with an updated comment.
|
||||
keyName := keyNames[0]
|
||||
addedKey := AddedKey{
|
||||
PrivateKey: testPrivateKeys[keyName],
|
||||
Comment: "comment updated",
|
||||
}
|
||||
err := k.Add(addedKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add key %q: %v", keyName, err)
|
||||
}
|
||||
// Check the that key is found and the comment was updated.
|
||||
keys, err := k.List()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list keys: %v", err)
|
||||
}
|
||||
if len(keys) != len(keyNames) {
|
||||
t.Fatalf("expected %d keys, got %d", len(keyNames), len(keys))
|
||||
}
|
||||
isFound := false
|
||||
for _, key := range keys {
|
||||
if key.Comment == addedKey.Comment {
|
||||
isFound = true
|
||||
}
|
||||
}
|
||||
if !isFound {
|
||||
t.Fatal("key with the updated comment not found")
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -506,7 +506,7 @@ func (s *server) insertIdentity(req []byte) error {
|
||||
switch record.Type {
|
||||
case ssh.KeyAlgoRSA:
|
||||
addedKey, err = parseRSAKey(req)
|
||||
case ssh.KeyAlgoDSA:
|
||||
case ssh.InsecureKeyAlgoDSA:
|
||||
addedKey, err = parseDSAKey(req)
|
||||
case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
|
||||
addedKey, err = parseECDSAKey(req)
|
||||
@@ -514,7 +514,7 @@ func (s *server) insertIdentity(req []byte) error {
|
||||
addedKey, err = parseEd25519Key(req)
|
||||
case ssh.CertAlgoRSAv01:
|
||||
addedKey, err = parseRSACert(req)
|
||||
case ssh.CertAlgoDSAv01:
|
||||
case ssh.InsecureCertAlgoDSAv01:
|
||||
addedKey, err = parseDSACert(req)
|
||||
case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
|
||||
addedKey, err = parseECDSACert(req)
|
||||
|
||||
+40
-27
@@ -20,14 +20,19 @@ import (
|
||||
// returned by MultiAlgorithmSigner and don't appear in the Signature.Format
|
||||
// field.
|
||||
const (
|
||||
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
||||
CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
|
||||
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||
CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
|
||||
CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
|
||||
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
||||
// Deprecated: DSA is only supported at insecure key sizes, and was removed
|
||||
// from major implementations.
|
||||
CertAlgoDSAv01 = InsecureCertAlgoDSAv01
|
||||
// Deprecated: DSA is only supported at insecure key sizes, and was removed
|
||||
// from major implementations.
|
||||
InsecureCertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
|
||||
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||
CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
|
||||
CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
|
||||
|
||||
// CertAlgoRSASHA256v01 and CertAlgoRSASHA512v01 can't appear as a
|
||||
// Certificate.Type (or PublicKey.Type), but only in
|
||||
@@ -228,7 +233,11 @@ func parseCert(in []byte, privAlgo string) (*Certificate, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The Type() function is intended to return only certificate key types, but
|
||||
// we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
|
||||
if _, ok := certKeyAlgoNames[k.Type()]; ok {
|
||||
return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", k.Type())
|
||||
}
|
||||
c.SignatureKey = k
|
||||
c.Signature, rest, ok = parseSignatureBody(g.Signature)
|
||||
if !ok || len(rest) > 0 {
|
||||
@@ -296,16 +305,13 @@ type CertChecker struct {
|
||||
SupportedCriticalOptions []string
|
||||
|
||||
// IsUserAuthority should return true if the key is recognized as an
|
||||
// authority for the given user certificate. This allows for
|
||||
// certificates to be signed by other certificates. This must be set
|
||||
// if this CertChecker will be checking user certificates.
|
||||
// authority for user certificate. This must be set if this CertChecker
|
||||
// will be checking user certificates.
|
||||
IsUserAuthority func(auth PublicKey) bool
|
||||
|
||||
// IsHostAuthority should report whether the key is recognized as
|
||||
// an authority for this host. This allows for certificates to be
|
||||
// signed by other keys, and for those other keys to only be valid
|
||||
// signers for particular hostnames. This must be set if this
|
||||
// CertChecker will be checking host certificates.
|
||||
// an authority for this host. This must be set if this CertChecker
|
||||
// will be checking host certificates.
|
||||
IsHostAuthority func(auth PublicKey, address string) bool
|
||||
|
||||
// Clock is used for verifying time stamps. If nil, time.Now
|
||||
@@ -442,12 +448,19 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
|
||||
// SignCert signs the certificate with an authority, setting the Nonce,
|
||||
// SignatureKey, and Signature fields. If the authority implements the
|
||||
// MultiAlgorithmSigner interface the first algorithm in the list is used. This
|
||||
// is useful if you want to sign with a specific algorithm.
|
||||
// is useful if you want to sign with a specific algorithm. As specified in
|
||||
// [SSH-CERTS], Section 2.1.1, authority can't be a [Certificate].
|
||||
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
||||
c.Nonce = make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand, c.Nonce); err != nil {
|
||||
return err
|
||||
}
|
||||
// The Type() function is intended to return only certificate key types, but
|
||||
// we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
|
||||
if _, ok := certKeyAlgoNames[authority.PublicKey().Type()]; ok {
|
||||
return fmt.Errorf("ssh: certificates cannot be used as authority (public key type %q)",
|
||||
authority.PublicKey().Type())
|
||||
}
|
||||
c.SignatureKey = authority.PublicKey()
|
||||
|
||||
if v, ok := authority.(MultiAlgorithmSigner); ok {
|
||||
@@ -485,16 +498,16 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
||||
//
|
||||
// This map must be kept in sync with the one in agent/client.go.
|
||||
var certKeyAlgoNames = map[string]string{
|
||||
CertAlgoRSAv01: KeyAlgoRSA,
|
||||
CertAlgoRSASHA256v01: KeyAlgoRSASHA256,
|
||||
CertAlgoRSASHA512v01: KeyAlgoRSASHA512,
|
||||
CertAlgoDSAv01: KeyAlgoDSA,
|
||||
CertAlgoECDSA256v01: KeyAlgoECDSA256,
|
||||
CertAlgoECDSA384v01: KeyAlgoECDSA384,
|
||||
CertAlgoECDSA521v01: KeyAlgoECDSA521,
|
||||
CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256,
|
||||
CertAlgoED25519v01: KeyAlgoED25519,
|
||||
CertAlgoSKED25519v01: KeyAlgoSKED25519,
|
||||
CertAlgoRSAv01: KeyAlgoRSA,
|
||||
CertAlgoRSASHA256v01: KeyAlgoRSASHA256,
|
||||
CertAlgoRSASHA512v01: KeyAlgoRSASHA512,
|
||||
InsecureCertAlgoDSAv01: InsecureKeyAlgoDSA,
|
||||
CertAlgoECDSA256v01: KeyAlgoECDSA256,
|
||||
CertAlgoECDSA384v01: KeyAlgoECDSA384,
|
||||
CertAlgoECDSA521v01: KeyAlgoECDSA521,
|
||||
CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256,
|
||||
CertAlgoED25519v01: KeyAlgoED25519,
|
||||
CertAlgoSKED25519v01: KeyAlgoSKED25519,
|
||||
}
|
||||
|
||||
// underlyingAlgo returns the signature algorithm associated with algo (which is
|
||||
|
||||
+36
-9
@@ -15,14 +15,12 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh/testdata"
|
||||
)
|
||||
|
||||
// Cert generated by ssh-keygen 6.0p1 Debian-4.
|
||||
// % ssh-keygen -s ca-key -I test user-key
|
||||
const exampleSSHCert = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb1srW/W3ZDjYAO45xLYAwzHBDLsJ4Ux6ICFIkTjb1LEAAAADAQABAAAAYQCkoR51poH0wE8w72cqSB8Sszx+vAhzcMdCO0wqHTj7UNENHWEXGrU0E0UQekD7U+yhkhtoyjbPOVIP7hNa6aRk/ezdh/iUnCIt4Jt1v3Z1h1P+hA4QuYFMHNB+rmjPwAcAAAAAAAAAAAAAAAEAAAAEdGVzdAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAHcAAAAHc3NoLXJzYQAAAAMBAAEAAABhANFS2kaktpSGc+CcmEKPyw9mJC4nZKxHKTgLVZeaGbFZOvJTNzBspQHdy7Q1uKSfktxpgjZnksiu/tFF9ngyY2KFoc+U88ya95IZUycBGCUbBQ8+bhDtw/icdDGQD5WnUwAAAG8AAAAHc3NoLXJzYQAAAGC8Y9Z2LQKhIhxf52773XaWrXdxP0t3GBVo4A10vUWiYoAGepr6rQIoGGXFxT4B9Gp+nEBJjOwKDXPrAevow0T9ca8gZN+0ykbhSrXLE5Ao48rqr3zP4O1/9P7e6gp0gw8=`
|
||||
|
||||
func TestParseCert(t *testing.T) {
|
||||
authKeyBytes := []byte(exampleSSHCert)
|
||||
authKeyBytes := bytes.TrimSuffix(testdata.SSHCertificates["rsa"], []byte(" host.example.com\n"))
|
||||
|
||||
key, _, _, rest, err := ParseAuthorizedKey(authKeyBytes)
|
||||
if err != nil {
|
||||
@@ -103,7 +101,7 @@ func TestParseCertWithOptions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateCert(t *testing.T) {
|
||||
key, _, _, _, err := ParseAuthorizedKey([]byte(exampleSSHCert))
|
||||
key, _, _, _, err := ParseAuthorizedKey(testdata.SSHCertificates["rsa-user-testcertificate"])
|
||||
if err != nil {
|
||||
t.Fatalf("ParseAuthorizedKey: %v", err)
|
||||
}
|
||||
@@ -116,7 +114,7 @@ func TestValidateCert(t *testing.T) {
|
||||
return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal())
|
||||
}
|
||||
|
||||
if err := checker.CheckCert("user", validCert); err != nil {
|
||||
if err := checker.CheckCert("testcertificate", validCert); err != nil {
|
||||
t.Errorf("Unable to validate certificate: %v", err)
|
||||
}
|
||||
invalidCert := &Certificate{
|
||||
@@ -125,7 +123,7 @@ func TestValidateCert(t *testing.T) {
|
||||
ValidBefore: CertTimeInfinity,
|
||||
Signature: &Signature{},
|
||||
}
|
||||
if err := checker.CheckCert("user", invalidCert); err == nil {
|
||||
if err := checker.CheckCert("testcertificate", invalidCert); err == nil {
|
||||
t.Error("Invalid cert signature passed validation")
|
||||
}
|
||||
}
|
||||
@@ -299,7 +297,7 @@ func TestCertTypes(t *testing.T) {
|
||||
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
|
||||
{"multiAlgoRSASignerSHA256", multiAlgoSignerSHA256, KeyAlgoRSASHA256},
|
||||
{"multiAlgoRSASignerSHA512", multiAlgoSignerSHA512, KeyAlgoRSASHA512},
|
||||
{CertAlgoDSAv01, testSigners["dsa"], ""},
|
||||
{InsecureCertAlgoDSAv01, testSigners["dsa"], ""},
|
||||
}
|
||||
|
||||
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
@@ -406,3 +404,32 @@ func TestCertSignWithMultiAlgorithmSigner(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertSignWithCertificate(t *testing.T) {
|
||||
cert := &Certificate{
|
||||
Key: testPublicKeys["rsa"],
|
||||
ValidBefore: CertTimeInfinity,
|
||||
CertType: UserCert,
|
||||
}
|
||||
if err := cert.SignCert(rand.Reader, testSigners["ecdsa"]); err != nil {
|
||||
t.Fatalf("SignCert: %v", err)
|
||||
}
|
||||
signer, err := NewSignerWithAlgorithms(testSigners["rsa"].(AlgorithmSigner), []string{KeyAlgoRSASHA256})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
certSigner, err := NewCertSigner(cert, signer)
|
||||
if err != nil {
|
||||
t.Fatalf("NewCertSigner: %v", err)
|
||||
}
|
||||
|
||||
cert1 := &Certificate{
|
||||
Key: testPublicKeys["ecdsa"],
|
||||
ValidBefore: CertTimeInfinity,
|
||||
CertType: UserCert,
|
||||
}
|
||||
|
||||
if err := cert1.SignCert(rand.Reader, certSigner); err == nil {
|
||||
t.Fatal("successfully signed a certificate using another certificate, it is expected to fail")
|
||||
}
|
||||
}
|
||||
|
||||
+19
-21
@@ -58,11 +58,11 @@ func newRC4(key, iv []byte) (cipher.Stream, error) {
|
||||
type cipherMode struct {
|
||||
keySize int
|
||||
ivSize int
|
||||
create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error)
|
||||
create func(key, iv []byte, macKey []byte, algs DirectionAlgorithms) (packetCipher, error)
|
||||
}
|
||||
|
||||
func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
return func(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
|
||||
return func(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
|
||||
stream, err := createFunc(key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -98,36 +98,36 @@ func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream,
|
||||
var cipherModes = map[string]*cipherMode{
|
||||
// Ciphers from RFC 4344, which introduced many CTR-based ciphers. Algorithms
|
||||
// are defined in the order specified in the RFC.
|
||||
"aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
"aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
"aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
CipherAES128CTR: {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
CipherAES192CTR: {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
CipherAES256CTR: {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
|
||||
// Ciphers from RFC 4345, which introduces security-improved arcfour ciphers.
|
||||
// They are defined in the order specified in the RFC.
|
||||
"arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
|
||||
"arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
|
||||
InsecureCipherRC4128: {16, 0, streamCipherMode(1536, newRC4)},
|
||||
InsecureCipherRC4256: {32, 0, streamCipherMode(1536, newRC4)},
|
||||
|
||||
// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
|
||||
// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
|
||||
// RC4) has problems with weak keys, and should be used with caution."
|
||||
// RFC 4345 introduces improved versions of Arcfour.
|
||||
"arcfour": {16, 0, streamCipherMode(0, newRC4)},
|
||||
InsecureCipherRC4: {16, 0, streamCipherMode(0, newRC4)},
|
||||
|
||||
// AEAD ciphers
|
||||
gcm128CipherID: {16, 12, newGCMCipher},
|
||||
gcm256CipherID: {32, 12, newGCMCipher},
|
||||
chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
|
||||
CipherAES128GCM: {16, 12, newGCMCipher},
|
||||
CipherAES256GCM: {32, 12, newGCMCipher},
|
||||
CipherChaCha20Poly1305: {64, 0, newChaCha20Cipher},
|
||||
|
||||
// CBC mode is insecure and so is not included in the default config.
|
||||
// (See https://www.ieee-security.org/TC/SP2013/papers/4977a526.pdf). If absolutely
|
||||
// needed, it's possible to specify a custom Config to enable it.
|
||||
// You should expect that an active attacker can recover plaintext if
|
||||
// you do.
|
||||
aes128cbcID: {16, aes.BlockSize, newAESCBCCipher},
|
||||
InsecureCipherAES128CBC: {16, aes.BlockSize, newAESCBCCipher},
|
||||
|
||||
// 3des-cbc is insecure and is not included in the default
|
||||
// config.
|
||||
tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher},
|
||||
InsecureCipherTripleDESCBC: {24, des.BlockSize, newTripleDESCBCCipher},
|
||||
}
|
||||
|
||||
// prefixLen is the length of the packet prefix that contains the packet length
|
||||
@@ -307,7 +307,7 @@ type gcmCipher struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
|
||||
func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs DirectionAlgorithms) (packetCipher, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -429,7 +429,7 @@ type cbcCipher struct {
|
||||
oracleCamouflage uint32
|
||||
}
|
||||
|
||||
func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
|
||||
cbc := &cbcCipher{
|
||||
mac: macModes[algs.MAC].new(macKey),
|
||||
decrypter: cipher.NewCBCDecrypter(c, iv),
|
||||
@@ -443,7 +443,7 @@ func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorith
|
||||
return cbc, nil
|
||||
}
|
||||
|
||||
func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
func newAESCBCCipher(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -457,7 +457,7 @@ func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCi
|
||||
return cbc, nil
|
||||
}
|
||||
|
||||
func newTripleDESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
func newTripleDESCBCCipher(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
|
||||
c, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -635,8 +635,6 @@ func (c *cbcCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader
|
||||
return nil
|
||||
}
|
||||
|
||||
const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
|
||||
|
||||
// chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com
|
||||
// AEAD, which is described here:
|
||||
//
|
||||
@@ -650,7 +648,7 @@ type chacha20Poly1305Cipher struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
|
||||
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs DirectionAlgorithms) (packetCipher, error) {
|
||||
if len(key) != 64 {
|
||||
panic(len(key))
|
||||
}
|
||||
|
||||
+14
-14
@@ -22,7 +22,7 @@ func TestDefaultCiphersExist(t *testing.T) {
|
||||
t.Errorf("supported cipher %q is unknown", cipherAlgo)
|
||||
}
|
||||
}
|
||||
for _, cipherAlgo := range preferredCiphers {
|
||||
for _, cipherAlgo := range defaultCiphers {
|
||||
if _, ok := cipherModes[cipherAlgo]; !ok {
|
||||
t.Errorf("preferred cipher %q is unknown", cipherAlgo)
|
||||
}
|
||||
@@ -30,8 +30,8 @@ func TestDefaultCiphersExist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPacketCiphers(t *testing.T) {
|
||||
defaultMac := "hmac-sha2-256"
|
||||
defaultCipher := "aes128-ctr"
|
||||
defaultMac := HMACSHA256
|
||||
defaultCipher := CipherAES128CTR
|
||||
for cipher := range cipherModes {
|
||||
t.Run("cipher="+cipher,
|
||||
func(t *testing.T) { testPacketCipher(t, cipher, defaultMac) })
|
||||
@@ -44,10 +44,10 @@ func TestPacketCiphers(t *testing.T) {
|
||||
|
||||
func testPacketCipher(t *testing.T, cipher, mac string) {
|
||||
kr := &kexResult{Hash: crypto.SHA1}
|
||||
algs := directionAlgorithms{
|
||||
algs := DirectionAlgorithms{
|
||||
Cipher: cipher,
|
||||
MAC: mac,
|
||||
Compression: "none",
|
||||
compression: compressionNone,
|
||||
}
|
||||
client, err := newPacketCipher(clientKeys, algs, kr)
|
||||
if err != nil {
|
||||
@@ -77,10 +77,10 @@ func testPacketCipher(t *testing.T, cipher, mac string) {
|
||||
|
||||
func TestCBCOracleCounterMeasure(t *testing.T) {
|
||||
kr := &kexResult{Hash: crypto.SHA1}
|
||||
algs := directionAlgorithms{
|
||||
Cipher: aes128cbcID,
|
||||
MAC: "hmac-sha1",
|
||||
Compression: "none",
|
||||
algs := DirectionAlgorithms{
|
||||
Cipher: InsecureCipherAES128CBC,
|
||||
MAC: HMACSHA1,
|
||||
compression: compressionNone,
|
||||
}
|
||||
client, err := newPacketCipher(clientKeys, algs, kr)
|
||||
if err != nil {
|
||||
@@ -141,7 +141,7 @@ func TestCVE202143565(t *testing.T) {
|
||||
constructPacket func(packetCipher) io.Reader
|
||||
}{
|
||||
{
|
||||
cipher: gcm128CipherID,
|
||||
cipher: CipherAES128GCM,
|
||||
constructPacket: func(client packetCipher) io.Reader {
|
||||
internalCipher := client.(*gcmCipher)
|
||||
b := &bytes.Buffer{}
|
||||
@@ -159,7 +159,7 @@ func TestCVE202143565(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
cipher: chacha20Poly1305ID,
|
||||
cipher: CipherChaCha20Poly1305,
|
||||
constructPacket: func(client packetCipher) io.Reader {
|
||||
internalCipher := client.(*chacha20Poly1305Cipher)
|
||||
b := &bytes.Buffer{}
|
||||
@@ -201,13 +201,13 @@ func TestCVE202143565(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
mac := "hmac-sha2-256"
|
||||
mac := HMACSHA256
|
||||
|
||||
kr := &kexResult{Hash: crypto.SHA1}
|
||||
algs := directionAlgorithms{
|
||||
algs := DirectionAlgorithms{
|
||||
Cipher: tc.cipher,
|
||||
MAC: mac,
|
||||
Compression: "none",
|
||||
compression: compressionNone,
|
||||
}
|
||||
client, err := newPacketCipher(clientKeys, algs, kr)
|
||||
if err != nil {
|
||||
|
||||
@@ -110,6 +110,7 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
|
||||
}
|
||||
|
||||
c.sessionID = c.transport.getSessionID()
|
||||
c.algorithms = c.transport.getAlgorithms()
|
||||
return c.clientAuthenticate(config)
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -289,7 +289,7 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiA
|
||||
}
|
||||
}
|
||||
|
||||
algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos)
|
||||
algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos, true)
|
||||
if err != nil {
|
||||
// If there is no overlap, return the fallback algorithm to support
|
||||
// servers that fail to list all supported algorithms.
|
||||
@@ -555,6 +555,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
}
|
||||
|
||||
gotMsgExtInfo := false
|
||||
gotUserAuthInfoRequest := false
|
||||
for {
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
@@ -585,6 +586,9 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
if msg.PartialSuccess {
|
||||
return authPartialSuccess, msg.Methods, nil
|
||||
}
|
||||
if !gotUserAuthInfoRequest {
|
||||
return authFailure, msg.Methods, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
|
||||
}
|
||||
return authFailure, msg.Methods, nil
|
||||
case msgUserAuthSuccess:
|
||||
return authSuccess, nil, nil
|
||||
@@ -596,6 +600,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
gotUserAuthInfoRequest = true
|
||||
|
||||
// Manually unpack the prompt/echo pairs.
|
||||
rest := msg.Prompts
|
||||
|
||||
+112
-9
@@ -38,7 +38,7 @@ func tryAuth(t *testing.T, config *ClientConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// tryAuth runs a handshake with a given config against an SSH server
|
||||
// tryAuthWithGSSAPIWithMICConfig runs a handshake with a given config against an SSH server
|
||||
// with a given GSSAPIWithMICConfig and config serverConfig. Returns both client and server side errors.
|
||||
func tryAuthWithGSSAPIWithMICConfig(t *testing.T, clientConfig *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) error {
|
||||
err, _ := tryAuthBothSides(t, clientConfig, gssAPIWithMICConfig)
|
||||
@@ -324,6 +324,9 @@ func TestMethodInvalidAlgorithm(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientHMAC(t *testing.T) {
|
||||
supportedAlgos := SupportedAlgorithms()
|
||||
insecureAlgos := InsecureAlgorithms()
|
||||
supportedMACs := append(supportedAlgos.MACs, insecureAlgos.MACs...)
|
||||
for _, mac := range supportedMACs {
|
||||
config := &ClientConfig{
|
||||
User: "testuser",
|
||||
@@ -349,7 +352,7 @@ func TestClientUnsupportedCipher(t *testing.T) {
|
||||
PublicKeys(),
|
||||
},
|
||||
Config: Config{
|
||||
Ciphers: []string{"aes128-cbc"}, // not currently supported
|
||||
Ciphers: []string{"unsupported-cipher"}, // not currently supported
|
||||
},
|
||||
}
|
||||
if err := tryAuth(t, config); err == nil {
|
||||
@@ -641,17 +644,28 @@ func TestClientAuthMaxAuthTries(t *testing.T) {
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
go newServer(c1, serverConfig)
|
||||
_, _, _, err = NewClientConn(c2, "", clientConfig)
|
||||
if tries > 2 {
|
||||
if err == nil {
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
_, err := newServer(c1, serverConfig)
|
||||
errCh <- err
|
||||
}()
|
||||
_, _, _, cliErr := NewClientConn(c2, "", clientConfig)
|
||||
srvErr := <-errCh
|
||||
|
||||
if tries > serverConfig.MaxAuthTries {
|
||||
if cliErr == nil {
|
||||
t.Fatalf("client: got no error, want %s", expectedErr)
|
||||
} else if err.Error() != expectedErr.Error() {
|
||||
} else if cliErr.Error() != expectedErr.Error() {
|
||||
t.Fatalf("client: got %s, want %s", err, expectedErr)
|
||||
}
|
||||
var authErr *ServerAuthError
|
||||
if !errors.As(srvErr, &authErr) {
|
||||
t.Errorf("expected ServerAuthError, got: %v", srvErr)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("client: got %s, want no error", err)
|
||||
if cliErr != nil {
|
||||
t.Fatalf("client: got %s, want no error", cliErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1282,3 +1296,92 @@ func TestCertAuthOpenSSHCompat(t *testing.T) {
|
||||
t.Fatalf("unable to dial remote side: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyboardInteractiveAuthEarlyFail(t *testing.T) {
|
||||
const maxAuthTries = 2
|
||||
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
// Start testserver
|
||||
serverConfig := &ServerConfig{
|
||||
MaxAuthTries: maxAuthTries,
|
||||
KeyboardInteractiveCallback: func(c ConnMetadata,
|
||||
client KeyboardInteractiveChallenge) (*Permissions, error) {
|
||||
// Fail keyboard-interactive authentication early before
|
||||
// any prompt is sent to client.
|
||||
return nil, errors.New("keyboard-interactive auth failed")
|
||||
},
|
||||
PasswordCallback: func(c ConnMetadata,
|
||||
pass []byte) (*Permissions, error) {
|
||||
if string(pass) == clientPassword {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("password auth failed")
|
||||
},
|
||||
}
|
||||
serverConfig.AddHostKey(testSigners["rsa"])
|
||||
|
||||
serverDone := make(chan struct{})
|
||||
go func() {
|
||||
defer func() { serverDone <- struct{}{} }()
|
||||
conn, chans, reqs, err := NewServerConn(c2, serverConfig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = conn.Close()
|
||||
|
||||
discarderDone := make(chan struct{})
|
||||
go func() {
|
||||
defer func() { discarderDone <- struct{}{} }()
|
||||
DiscardRequests(reqs)
|
||||
}()
|
||||
for newChannel := range chans {
|
||||
newChannel.Reject(Prohibited,
|
||||
"testserver not accepting requests")
|
||||
}
|
||||
|
||||
<-discarderDone
|
||||
}()
|
||||
|
||||
// Connect to testserver, expect KeyboardInteractive() to be not called,
|
||||
// PasswordCallback() to be called and connection to succeed.
|
||||
passwordCallbackCalled := false
|
||||
clientConfig := &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []AuthMethod{
|
||||
RetryableAuthMethod(KeyboardInteractive(func(name,
|
||||
instruction string, questions []string,
|
||||
echos []bool) ([]string, error) {
|
||||
t.Errorf("unexpected call to KeyboardInteractive()")
|
||||
return []string{clientPassword}, nil
|
||||
}), maxAuthTries),
|
||||
RetryableAuthMethod(PasswordCallback(func() (secret string,
|
||||
err error) {
|
||||
t.Logf("PasswordCallback()")
|
||||
passwordCallbackCalled = true
|
||||
return clientPassword, nil
|
||||
}), maxAuthTries),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
conn, _, _, err := NewClientConn(c1, "", clientConfig)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected NewClientConn() error: %v", err)
|
||||
}
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
// Wait for server to finish.
|
||||
<-serverDone
|
||||
|
||||
if !passwordCallbackCalled {
|
||||
t.Errorf("expected PasswordCallback() to be called")
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -292,7 +292,7 @@ func TestUnsupportedAlgorithm(t *testing.T) {
|
||||
{
|
||||
"unsupported and supported KEXs",
|
||||
Config{
|
||||
KeyExchanges: []string{"unsupported", kexAlgoCurve25519SHA256},
|
||||
KeyExchanges: []string{"unsupported", KeyExchangeCurve25519},
|
||||
},
|
||||
"",
|
||||
},
|
||||
@@ -306,7 +306,7 @@ func TestUnsupportedAlgorithm(t *testing.T) {
|
||||
{
|
||||
"unsupported and supported ciphers",
|
||||
Config{
|
||||
Ciphers: []string{"unsupported", chacha20Poly1305ID},
|
||||
Ciphers: []string{"unsupported", CipherChaCha20Poly1305},
|
||||
},
|
||||
"",
|
||||
},
|
||||
@@ -315,16 +315,16 @@ func TestUnsupportedAlgorithm(t *testing.T) {
|
||||
Config{
|
||||
MACs: []string{"unsupported"},
|
||||
// MAC is used for non AAED ciphers.
|
||||
Ciphers: []string{"aes256-ctr"},
|
||||
Ciphers: []string{CipherAES256CTR},
|
||||
},
|
||||
"no common algorithm",
|
||||
},
|
||||
{
|
||||
"unsupported and supported MACs",
|
||||
Config{
|
||||
MACs: []string{"unsupported", "hmac-sha2-256-etm@openssh.com"},
|
||||
MACs: []string{"unsupported", HMACSHA256ETM},
|
||||
// MAC is used for non AAED ciphers.
|
||||
Ciphers: []string{"aes256-ctr"},
|
||||
Ciphers: []string{CipherAES256CTR},
|
||||
},
|
||||
"",
|
||||
},
|
||||
|
||||
+309
-107
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
_ "crypto/sha1"
|
||||
@@ -24,69 +25,260 @@ const (
|
||||
serviceSSH = "ssh-connection"
|
||||
)
|
||||
|
||||
// supportedCiphers lists ciphers we support but might not recommend.
|
||||
var supportedCiphers = []string{
|
||||
"aes128-ctr", "aes192-ctr", "aes256-ctr",
|
||||
"aes128-gcm@openssh.com", gcm256CipherID,
|
||||
chacha20Poly1305ID,
|
||||
"arcfour256", "arcfour128", "arcfour",
|
||||
aes128cbcID,
|
||||
tripledescbcID,
|
||||
// The ciphers currently or previously implemented by this library, to use in
|
||||
// [Config.Ciphers]. For a list, see the [Algorithms.Ciphers] returned by
|
||||
// [SupportedAlgorithms] or [InsecureAlgorithms].
|
||||
const (
|
||||
CipherAES128GCM = "aes128-gcm@openssh.com"
|
||||
CipherAES256GCM = "aes256-gcm@openssh.com"
|
||||
CipherChaCha20Poly1305 = "chacha20-poly1305@openssh.com"
|
||||
CipherAES128CTR = "aes128-ctr"
|
||||
CipherAES192CTR = "aes192-ctr"
|
||||
CipherAES256CTR = "aes256-ctr"
|
||||
InsecureCipherAES128CBC = "aes128-cbc"
|
||||
InsecureCipherTripleDESCBC = "3des-cbc"
|
||||
InsecureCipherRC4 = "arcfour"
|
||||
InsecureCipherRC4128 = "arcfour128"
|
||||
InsecureCipherRC4256 = "arcfour256"
|
||||
)
|
||||
|
||||
// The key exchanges currently or previously implemented by this library, to use
|
||||
// in [Config.KeyExchanges]. For a list, see the
|
||||
// [Algorithms.KeyExchanges] returned by [SupportedAlgorithms] or
|
||||
// [InsecureAlgorithms].
|
||||
const (
|
||||
InsecureKeyExchangeDH1SHA1 = "diffie-hellman-group1-sha1"
|
||||
InsecureKeyExchangeDH14SHA1 = "diffie-hellman-group14-sha1"
|
||||
KeyExchangeDH14SHA256 = "diffie-hellman-group14-sha256"
|
||||
KeyExchangeDH16SHA512 = "diffie-hellman-group16-sha512"
|
||||
KeyExchangeECDHP256 = "ecdh-sha2-nistp256"
|
||||
KeyExchangeECDHP384 = "ecdh-sha2-nistp384"
|
||||
KeyExchangeECDHP521 = "ecdh-sha2-nistp521"
|
||||
KeyExchangeCurve25519 = "curve25519-sha256"
|
||||
InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
|
||||
KeyExchangeDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
|
||||
// KeyExchangeMLKEM768X25519 is supported from Go 1.24.
|
||||
KeyExchangeMLKEM768X25519 = "mlkem768x25519-sha256"
|
||||
|
||||
// An alias for KeyExchangeCurve25519SHA256. This kex ID will be added if
|
||||
// KeyExchangeCurve25519SHA256 is requested for backward compatibility with
|
||||
// OpenSSH versions up to 7.2.
|
||||
keyExchangeCurve25519LibSSH = "curve25519-sha256@libssh.org"
|
||||
)
|
||||
|
||||
// The message authentication code (MAC) currently or previously implemented by
|
||||
// this library, to use in [Config.MACs]. For a list, see the
|
||||
// [Algorithms.MACs] returned by [SupportedAlgorithms] or
|
||||
// [InsecureAlgorithms].
|
||||
const (
|
||||
HMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
|
||||
HMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
|
||||
HMACSHA256 = "hmac-sha2-256"
|
||||
HMACSHA512 = "hmac-sha2-512"
|
||||
HMACSHA1 = "hmac-sha1"
|
||||
InsecureHMACSHA196 = "hmac-sha1-96"
|
||||
)
|
||||
|
||||
var (
|
||||
// supportedKexAlgos specifies key-exchange algorithms implemented by this
|
||||
// package in preference order, excluding those with security issues.
|
||||
supportedKexAlgos = []string{
|
||||
KeyExchangeMLKEM768X25519,
|
||||
KeyExchangeCurve25519,
|
||||
KeyExchangeECDHP256,
|
||||
KeyExchangeECDHP384,
|
||||
KeyExchangeECDHP521,
|
||||
KeyExchangeDH14SHA256,
|
||||
KeyExchangeDH16SHA512,
|
||||
KeyExchangeDHGEXSHA256,
|
||||
}
|
||||
// defaultKexAlgos specifies the default preference for key-exchange
|
||||
// algorithms in preference order.
|
||||
defaultKexAlgos = []string{
|
||||
KeyExchangeMLKEM768X25519,
|
||||
KeyExchangeCurve25519,
|
||||
KeyExchangeECDHP256,
|
||||
KeyExchangeECDHP384,
|
||||
KeyExchangeECDHP521,
|
||||
KeyExchangeDH14SHA256,
|
||||
InsecureKeyExchangeDH14SHA1,
|
||||
}
|
||||
// insecureKexAlgos specifies key-exchange algorithms implemented by this
|
||||
// package and which have security issues.
|
||||
insecureKexAlgos = []string{
|
||||
InsecureKeyExchangeDH14SHA1,
|
||||
InsecureKeyExchangeDH1SHA1,
|
||||
InsecureKeyExchangeDHGEXSHA1,
|
||||
}
|
||||
// supportedCiphers specifies cipher algorithms implemented by this package
|
||||
// in preference order, excluding those with security issues.
|
||||
supportedCiphers = []string{
|
||||
CipherAES128GCM,
|
||||
CipherAES256GCM,
|
||||
CipherChaCha20Poly1305,
|
||||
CipherAES128CTR,
|
||||
CipherAES192CTR,
|
||||
CipherAES256CTR,
|
||||
}
|
||||
// defaultCiphers specifies the default preference for ciphers algorithms
|
||||
// in preference order.
|
||||
defaultCiphers = supportedCiphers
|
||||
// insecureCiphers specifies cipher algorithms implemented by this
|
||||
// package and which have security issues.
|
||||
insecureCiphers = []string{
|
||||
InsecureCipherAES128CBC,
|
||||
InsecureCipherTripleDESCBC,
|
||||
InsecureCipherRC4256,
|
||||
InsecureCipherRC4128,
|
||||
InsecureCipherRC4,
|
||||
}
|
||||
// supportedMACs specifies MAC algorithms implemented by this package in
|
||||
// preference order, excluding those with security issues.
|
||||
supportedMACs = []string{
|
||||
HMACSHA256ETM,
|
||||
HMACSHA512ETM,
|
||||
HMACSHA256,
|
||||
HMACSHA512,
|
||||
HMACSHA1,
|
||||
}
|
||||
// defaultMACs specifies the default preference for MAC algorithms in
|
||||
// preference order.
|
||||
defaultMACs = []string{
|
||||
HMACSHA256ETM,
|
||||
HMACSHA512ETM,
|
||||
HMACSHA256,
|
||||
HMACSHA512,
|
||||
HMACSHA1,
|
||||
InsecureHMACSHA196,
|
||||
}
|
||||
// insecureMACs specifies MAC algorithms implemented by this
|
||||
// package and which have security issues.
|
||||
insecureMACs = []string{
|
||||
InsecureHMACSHA196,
|
||||
}
|
||||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e.
|
||||
// methods of authenticating servers) implemented by this package in
|
||||
// preference order, excluding those with security issues.
|
||||
supportedHostKeyAlgos = []string{
|
||||
CertAlgoRSASHA256v01,
|
||||
CertAlgoRSASHA512v01,
|
||||
CertAlgoECDSA256v01,
|
||||
CertAlgoECDSA384v01,
|
||||
CertAlgoECDSA521v01,
|
||||
CertAlgoED25519v01,
|
||||
KeyAlgoRSASHA256,
|
||||
KeyAlgoRSASHA512,
|
||||
KeyAlgoECDSA256,
|
||||
KeyAlgoECDSA384,
|
||||
KeyAlgoECDSA521,
|
||||
KeyAlgoED25519,
|
||||
}
|
||||
// defaultHostKeyAlgos specifies the default preference for host-key
|
||||
// algorithms in preference order.
|
||||
defaultHostKeyAlgos = []string{
|
||||
CertAlgoRSASHA256v01,
|
||||
CertAlgoRSASHA512v01,
|
||||
CertAlgoRSAv01,
|
||||
InsecureCertAlgoDSAv01,
|
||||
CertAlgoECDSA256v01,
|
||||
CertAlgoECDSA384v01,
|
||||
CertAlgoECDSA521v01,
|
||||
CertAlgoED25519v01,
|
||||
KeyAlgoECDSA256,
|
||||
KeyAlgoECDSA384,
|
||||
KeyAlgoECDSA521,
|
||||
KeyAlgoRSASHA256,
|
||||
KeyAlgoRSASHA512,
|
||||
KeyAlgoRSA,
|
||||
InsecureKeyAlgoDSA,
|
||||
KeyAlgoED25519,
|
||||
}
|
||||
// insecureHostKeyAlgos specifies host-key algorithms implemented by this
|
||||
// package and which have security issues.
|
||||
insecureHostKeyAlgos = []string{
|
||||
KeyAlgoRSA,
|
||||
InsecureKeyAlgoDSA,
|
||||
CertAlgoRSAv01,
|
||||
InsecureCertAlgoDSAv01,
|
||||
}
|
||||
// supportedPubKeyAuthAlgos specifies the supported client public key
|
||||
// authentication algorithms. Note that this doesn't include certificate
|
||||
// types since those use the underlying algorithm. Order is irrelevant.
|
||||
supportedPubKeyAuthAlgos = []string{
|
||||
KeyAlgoED25519,
|
||||
KeyAlgoSKED25519,
|
||||
KeyAlgoSKECDSA256,
|
||||
KeyAlgoECDSA256,
|
||||
KeyAlgoECDSA384,
|
||||
KeyAlgoECDSA521,
|
||||
KeyAlgoRSASHA256,
|
||||
KeyAlgoRSASHA512,
|
||||
}
|
||||
|
||||
// defaultPubKeyAuthAlgos specifies the preferred client public key
|
||||
// authentication algorithms. This list is sent to the client if it supports
|
||||
// the server-sig-algs extension. Order is irrelevant.
|
||||
defaultPubKeyAuthAlgos = []string{
|
||||
KeyAlgoED25519,
|
||||
KeyAlgoSKED25519,
|
||||
KeyAlgoSKECDSA256,
|
||||
KeyAlgoECDSA256,
|
||||
KeyAlgoECDSA384,
|
||||
KeyAlgoECDSA521,
|
||||
KeyAlgoRSASHA256,
|
||||
KeyAlgoRSASHA512,
|
||||
KeyAlgoRSA,
|
||||
InsecureKeyAlgoDSA,
|
||||
}
|
||||
// insecurePubKeyAuthAlgos specifies client public key authentication
|
||||
// algorithms implemented by this package and which have security issues.
|
||||
insecurePubKeyAuthAlgos = []string{
|
||||
KeyAlgoRSA,
|
||||
InsecureKeyAlgoDSA,
|
||||
}
|
||||
)
|
||||
|
||||
// NegotiatedAlgorithms defines algorithms negotiated between client and server.
|
||||
type NegotiatedAlgorithms struct {
|
||||
KeyExchange string
|
||||
HostKey string
|
||||
Read DirectionAlgorithms
|
||||
Write DirectionAlgorithms
|
||||
}
|
||||
|
||||
// preferredCiphers specifies the default preference for ciphers.
|
||||
var preferredCiphers = []string{
|
||||
"aes128-gcm@openssh.com", gcm256CipherID,
|
||||
chacha20Poly1305ID,
|
||||
"aes128-ctr", "aes192-ctr", "aes256-ctr",
|
||||
// Algorithms defines a set of algorithms that can be configured in the client
|
||||
// or server config for negotiation during a handshake.
|
||||
type Algorithms struct {
|
||||
KeyExchanges []string
|
||||
Ciphers []string
|
||||
MACs []string
|
||||
HostKeys []string
|
||||
PublicKeyAuths []string
|
||||
}
|
||||
|
||||
// supportedKexAlgos specifies the supported key-exchange algorithms in
|
||||
// preference order.
|
||||
var supportedKexAlgos = []string{
|
||||
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
|
||||
// P384 and P521 are not constant-time yet, but since we don't
|
||||
// reuse ephemeral keys, using them for ECDH should be OK.
|
||||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
||||
kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
|
||||
kexAlgoDH1SHA1,
|
||||
// SupportedAlgorithms returns algorithms currently implemented by this package,
|
||||
// excluding those with security issues, which are returned by
|
||||
// InsecureAlgorithms. The algorithms listed here are in preference order.
|
||||
func SupportedAlgorithms() Algorithms {
|
||||
return Algorithms{
|
||||
Ciphers: slices.Clone(supportedCiphers),
|
||||
MACs: slices.Clone(supportedMACs),
|
||||
KeyExchanges: slices.Clone(supportedKexAlgos),
|
||||
HostKeys: slices.Clone(supportedHostKeyAlgos),
|
||||
PublicKeyAuths: slices.Clone(supportedPubKeyAuthAlgos),
|
||||
}
|
||||
}
|
||||
|
||||
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
|
||||
// for the server half.
|
||||
var serverForbiddenKexAlgos = map[string]struct{}{
|
||||
kexAlgoDHGEXSHA1: {}, // server half implementation is only minimal to satisfy the automated tests
|
||||
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
|
||||
}
|
||||
|
||||
// preferredKexAlgos specifies the default preference for key-exchange
|
||||
// algorithms in preference order. The diffie-hellman-group16-sha512 algorithm
|
||||
// is disabled by default because it is a bit slower than the others.
|
||||
var preferredKexAlgos = []string{
|
||||
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
|
||||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
||||
kexAlgoDH14SHA256, kexAlgoDH14SHA1,
|
||||
}
|
||||
|
||||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
|
||||
// of authenticating servers) in preference order.
|
||||
var supportedHostKeyAlgos = []string{
|
||||
CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
|
||||
|
||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||
KeyAlgoRSASHA256, KeyAlgoRSASHA512,
|
||||
KeyAlgoRSA, KeyAlgoDSA,
|
||||
|
||||
KeyAlgoED25519,
|
||||
}
|
||||
|
||||
// supportedMACs specifies a default set of MAC algorithms in preference order.
|
||||
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
|
||||
// because they have reached the end of their useful life.
|
||||
var supportedMACs = []string{
|
||||
"hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
|
||||
// InsecureAlgorithms returns algorithms currently implemented by this package
|
||||
// and which have security issues.
|
||||
func InsecureAlgorithms() Algorithms {
|
||||
return Algorithms{
|
||||
KeyExchanges: slices.Clone(insecureKexAlgos),
|
||||
Ciphers: slices.Clone(insecureCiphers),
|
||||
MACs: slices.Clone(insecureMACs),
|
||||
HostKeys: slices.Clone(insecureHostKeyAlgos),
|
||||
PublicKeyAuths: slices.Clone(insecurePubKeyAuthAlgos),
|
||||
}
|
||||
}
|
||||
|
||||
var supportedCompressions = []string{compressionNone}
|
||||
@@ -94,13 +286,13 @@ var supportedCompressions = []string{compressionNone}
|
||||
// hashFuncs keeps the mapping of supported signature algorithms to their
|
||||
// respective hashes needed for signing and verification.
|
||||
var hashFuncs = map[string]crypto.Hash{
|
||||
KeyAlgoRSA: crypto.SHA1,
|
||||
KeyAlgoRSASHA256: crypto.SHA256,
|
||||
KeyAlgoRSASHA512: crypto.SHA512,
|
||||
KeyAlgoDSA: crypto.SHA1,
|
||||
KeyAlgoECDSA256: crypto.SHA256,
|
||||
KeyAlgoECDSA384: crypto.SHA384,
|
||||
KeyAlgoECDSA521: crypto.SHA512,
|
||||
KeyAlgoRSA: crypto.SHA1,
|
||||
KeyAlgoRSASHA256: crypto.SHA256,
|
||||
KeyAlgoRSASHA512: crypto.SHA512,
|
||||
InsecureKeyAlgoDSA: crypto.SHA1,
|
||||
KeyAlgoECDSA256: crypto.SHA256,
|
||||
KeyAlgoECDSA384: crypto.SHA384,
|
||||
KeyAlgoECDSA521: crypto.SHA512,
|
||||
// KeyAlgoED25519 doesn't pre-hash.
|
||||
KeyAlgoSKECDSA256: crypto.SHA256,
|
||||
KeyAlgoSKED25519: crypto.SHA256,
|
||||
@@ -135,18 +327,6 @@ func isRSACert(algo string) bool {
|
||||
return isRSA(algo)
|
||||
}
|
||||
|
||||
// supportedPubKeyAuthAlgos specifies the supported client public key
|
||||
// authentication algorithms. Note that this doesn't include certificate types
|
||||
// since those use the underlying algorithm. This list is sent to the client if
|
||||
// it supports the server-sig-algs extension. Order is irrelevant.
|
||||
var supportedPubKeyAuthAlgos = []string{
|
||||
KeyAlgoED25519,
|
||||
KeyAlgoSKED25519, KeyAlgoSKECDSA256,
|
||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||
KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
|
||||
KeyAlgoDSA,
|
||||
}
|
||||
|
||||
// unexpectedMessageError results when the SSH message that we received didn't
|
||||
// match what we wanted.
|
||||
func unexpectedMessageError(expected, got uint8) error {
|
||||
@@ -158,7 +338,7 @@ func parseError(tag uint8) error {
|
||||
return fmt.Errorf("ssh: parse error in message type %d", tag)
|
||||
}
|
||||
|
||||
func findCommon(what string, client []string, server []string) (common string, err error) {
|
||||
func findCommon(what string, client []string, server []string, isClient bool) (string, error) {
|
||||
for _, c := range client {
|
||||
for _, s := range server {
|
||||
if c == s {
|
||||
@@ -166,23 +346,49 @@ 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)
|
||||
err := &AlgorithmNegotiationError{
|
||||
What: what,
|
||||
}
|
||||
if isClient {
|
||||
err.SupportedAlgorithms = client
|
||||
err.RequestedAlgorithms = server
|
||||
} else {
|
||||
err.SupportedAlgorithms = server
|
||||
err.RequestedAlgorithms = client
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// directionAlgorithms records algorithm choices in one direction (either read or write)
|
||||
type directionAlgorithms struct {
|
||||
// AlgorithmNegotiationError defines the error returned if the client and the
|
||||
// server cannot agree on an algorithm for key exchange, host key, cipher, MAC.
|
||||
type AlgorithmNegotiationError struct {
|
||||
What string
|
||||
// RequestedAlgorithms lists the algorithms supported by the peer.
|
||||
RequestedAlgorithms []string
|
||||
// SupportedAlgorithms lists the algorithms supported on our side.
|
||||
SupportedAlgorithms []string
|
||||
}
|
||||
|
||||
func (a *AlgorithmNegotiationError) Error() string {
|
||||
return fmt.Sprintf("ssh: no common algorithm for %s; we offered: %v, peer offered: %v",
|
||||
a.What, a.SupportedAlgorithms, a.RequestedAlgorithms)
|
||||
}
|
||||
|
||||
// DirectionAlgorithms defines the algorithms negotiated in one direction
|
||||
// (either read or write).
|
||||
type DirectionAlgorithms struct {
|
||||
Cipher string
|
||||
MAC string
|
||||
Compression string
|
||||
compression string
|
||||
}
|
||||
|
||||
// rekeyBytes returns a rekeying intervals in bytes.
|
||||
func (a *directionAlgorithms) rekeyBytes() int64 {
|
||||
func (a *DirectionAlgorithms) rekeyBytes() int64 {
|
||||
// According to RFC 4344 block ciphers should rekey after
|
||||
// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
|
||||
// 128.
|
||||
switch a.Cipher {
|
||||
case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcm128CipherID, gcm256CipherID, aes128cbcID:
|
||||
case CipherAES128CTR, CipherAES192CTR, CipherAES256CTR, CipherAES128GCM, CipherAES256GCM, InsecureCipherAES128CBC:
|
||||
return 16 * (1 << 32)
|
||||
|
||||
}
|
||||
@@ -192,66 +398,59 @@ func (a *directionAlgorithms) rekeyBytes() int64 {
|
||||
}
|
||||
|
||||
var aeadCiphers = map[string]bool{
|
||||
gcm128CipherID: true,
|
||||
gcm256CipherID: true,
|
||||
chacha20Poly1305ID: true,
|
||||
CipherAES128GCM: true,
|
||||
CipherAES256GCM: true,
|
||||
CipherChaCha20Poly1305: true,
|
||||
}
|
||||
|
||||
type algorithms struct {
|
||||
kex string
|
||||
hostKey string
|
||||
w directionAlgorithms
|
||||
r directionAlgorithms
|
||||
}
|
||||
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *NegotiatedAlgorithms, err error) {
|
||||
result := &NegotiatedAlgorithms{}
|
||||
|
||||
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
|
||||
result := &algorithms{}
|
||||
|
||||
result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
|
||||
result.KeyExchange, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
|
||||
result.HostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stoc, ctos := &result.w, &result.r
|
||||
stoc, ctos := &result.Write, &result.Read
|
||||
if isClient {
|
||||
ctos, stoc = stoc, ctos
|
||||
}
|
||||
|
||||
ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
|
||||
ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
|
||||
stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !aeadCiphers[ctos.Cipher] {
|
||||
ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
|
||||
ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !aeadCiphers[stoc.Cipher] {
|
||||
stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
|
||||
stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
|
||||
ctos.compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
|
||||
stoc.compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient, isClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -297,7 +496,7 @@ func (c *Config) SetDefaults() {
|
||||
c.Rand = rand.Reader
|
||||
}
|
||||
if c.Ciphers == nil {
|
||||
c.Ciphers = preferredCiphers
|
||||
c.Ciphers = defaultCiphers
|
||||
}
|
||||
var ciphers []string
|
||||
for _, c := range c.Ciphers {
|
||||
@@ -309,19 +508,22 @@ func (c *Config) SetDefaults() {
|
||||
c.Ciphers = ciphers
|
||||
|
||||
if c.KeyExchanges == nil {
|
||||
c.KeyExchanges = preferredKexAlgos
|
||||
c.KeyExchanges = defaultKexAlgos
|
||||
}
|
||||
var kexs []string
|
||||
for _, k := range c.KeyExchanges {
|
||||
if kexAlgoMap[k] != nil {
|
||||
// Ignore the KEX if we have no kexAlgoMap definition.
|
||||
kexs = append(kexs, k)
|
||||
if k == KeyExchangeCurve25519 && !contains(c.KeyExchanges, keyExchangeCurve25519LibSSH) {
|
||||
kexs = append(kexs, keyExchangeCurve25519LibSSH)
|
||||
}
|
||||
}
|
||||
}
|
||||
c.KeyExchanges = kexs
|
||||
|
||||
if c.MACs == nil {
|
||||
c.MACs = supportedMACs
|
||||
c.MACs = defaultMACs
|
||||
}
|
||||
var macs []string
|
||||
for _, m := range c.MACs {
|
||||
|
||||
+17
-17
@@ -51,33 +51,33 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
initDirAlgs := func(a *directionAlgorithms) {
|
||||
initDirAlgs := func(a *DirectionAlgorithms) {
|
||||
if a.Cipher == "" {
|
||||
a.Cipher = "cipher1"
|
||||
}
|
||||
if a.MAC == "" {
|
||||
a.MAC = "mac1"
|
||||
}
|
||||
if a.Compression == "" {
|
||||
a.Compression = "compression1"
|
||||
if a.compression == "" {
|
||||
a.compression = "compression1"
|
||||
}
|
||||
}
|
||||
|
||||
initAlgs := func(a *algorithms) {
|
||||
if a.kex == "" {
|
||||
a.kex = "kex1"
|
||||
initAlgs := func(a *NegotiatedAlgorithms) {
|
||||
if a.KeyExchange == "" {
|
||||
a.KeyExchange = "kex1"
|
||||
}
|
||||
if a.hostKey == "" {
|
||||
a.hostKey = "hostkey1"
|
||||
if a.HostKey == "" {
|
||||
a.HostKey = "hostkey1"
|
||||
}
|
||||
initDirAlgs(&a.r)
|
||||
initDirAlgs(&a.w)
|
||||
initDirAlgs(&a.Read)
|
||||
initDirAlgs(&a.Write)
|
||||
}
|
||||
|
||||
type testcase struct {
|
||||
name string
|
||||
clientIn, serverIn kexInitMsg
|
||||
wantClient, wantServer algorithms
|
||||
wantClient, wantServer NegotiatedAlgorithms
|
||||
wantErr bool
|
||||
}
|
||||
|
||||
@@ -120,19 +120,19 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
||||
CiphersClientServer: []string{"cipher2", "cipher1"},
|
||||
CiphersServerClient: []string{"cipher3", "cipher2"},
|
||||
},
|
||||
wantClient: algorithms{
|
||||
r: directionAlgorithms{
|
||||
wantClient: NegotiatedAlgorithms{
|
||||
Read: DirectionAlgorithms{
|
||||
Cipher: "cipher3",
|
||||
},
|
||||
w: directionAlgorithms{
|
||||
Write: DirectionAlgorithms{
|
||||
Cipher: "cipher2",
|
||||
},
|
||||
},
|
||||
wantServer: algorithms{
|
||||
w: directionAlgorithms{
|
||||
wantServer: NegotiatedAlgorithms{
|
||||
Write: DirectionAlgorithms{
|
||||
Cipher: "cipher3",
|
||||
},
|
||||
r: directionAlgorithms{
|
||||
Read: DirectionAlgorithms{
|
||||
Cipher: "cipher2",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -74,6 +74,13 @@ type Conn interface {
|
||||
// Disconnect
|
||||
}
|
||||
|
||||
// AlgorithmsConnMetadata is a ConnMetadata that can return the algorithms
|
||||
// negotiated between client and server.
|
||||
type AlgorithmsConnMetadata interface {
|
||||
ConnMetadata
|
||||
Algorithms() NegotiatedAlgorithms
|
||||
}
|
||||
|
||||
// DiscardRequests consumes and rejects all requests from the
|
||||
// passed-in channel.
|
||||
func DiscardRequests(in <-chan *Request) {
|
||||
@@ -106,6 +113,7 @@ type sshConn struct {
|
||||
sessionID []byte
|
||||
clientVersion []byte
|
||||
serverVersion []byte
|
||||
algorithms NegotiatedAlgorithms
|
||||
}
|
||||
|
||||
func dup(src []byte) []byte {
|
||||
@@ -141,3 +149,7 @@ func (c *sshConn) ClientVersion() []byte {
|
||||
func (c *sshConn) ServerVersion() []byte {
|
||||
return dup(c.serverVersion)
|
||||
}
|
||||
|
||||
func (c *sshConn) Algorithms() NegotiatedAlgorithms {
|
||||
return c.algorithms
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ References:
|
||||
[PROTOCOL]: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=HEAD
|
||||
[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
|
||||
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
|
||||
[SSH-CERTS]: https://datatracker.ietf.org/doc/html/draft-miller-ssh-cert-01
|
||||
|
||||
This package does not fall under the stability promise of the Go language itself,
|
||||
so its API may be changed when pressing needs arise.
|
||||
|
||||
@@ -154,6 +154,75 @@ func ExampleNewServerConn() {
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleServerConfig() {
|
||||
// Minimal ServerConfig with SHA-1 algoritms disabled and supporting only
|
||||
// public key authentication.
|
||||
|
||||
// The algorithms returned by ssh.SupportedAlgorithms() are different from
|
||||
// the default ones and do not include algorithms that are considered
|
||||
// insecure, such as those using SHA-1, returned by
|
||||
// ssh.InsecureAlgorithms().
|
||||
algorithms := ssh.SupportedAlgorithms()
|
||||
|
||||
// Public key authentication is done by comparing
|
||||
// the public key of a received connection
|
||||
// with the entries in the authorized_keys file.
|
||||
authorizedKeysBytes, err := os.ReadFile("authorized_keys")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load authorized_keys, err: %v", err)
|
||||
}
|
||||
|
||||
authorizedKeysMap := map[string]bool{}
|
||||
for len(authorizedKeysBytes) > 0 {
|
||||
pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
authorizedKeysMap[string(pubKey.Marshal())] = true
|
||||
authorizedKeysBytes = rest
|
||||
}
|
||||
|
||||
config := &ssh.ServerConfig{
|
||||
Config: ssh.Config{
|
||||
KeyExchanges: algorithms.KeyExchanges,
|
||||
Ciphers: algorithms.Ciphers,
|
||||
MACs: algorithms.MACs,
|
||||
},
|
||||
// The configured PublicKeyAuthAlgorithms do not contain SHA-1 based
|
||||
// signature formats, so if the client attempts to use them the
|
||||
// authentication will fail before calling the defined callback.
|
||||
PublicKeyAuthAlgorithms: algorithms.PublicKeyAuths,
|
||||
PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
if authorizedKeysMap[string(pubKey.Marshal())] {
|
||||
return &ssh.Permissions{
|
||||
// Record the public key used for authentication.
|
||||
Extensions: map[string]string{
|
||||
"pubkey-fp": ssh.FingerprintSHA256(pubKey),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown public key for %q", c.User())
|
||||
},
|
||||
}
|
||||
|
||||
privateBytes, err := os.ReadFile("id_rsa")
|
||||
if err != nil {
|
||||
log.Fatal("Failed to load private key: ", err)
|
||||
}
|
||||
|
||||
private, err := ssh.ParsePrivateKey(privateBytes)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to parse private key: ", err)
|
||||
}
|
||||
// Restrict host key algorithms to disable ssh-rsa.
|
||||
signer, err := ssh.NewSignerWithAlgorithms(private.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create private key with restricted algorithms: ", err)
|
||||
}
|
||||
config.AddHostKey(signer)
|
||||
}
|
||||
|
||||
func ExampleServerConfig_AddHostKey() {
|
||||
// Minimal ServerConfig supporting only password authentication.
|
||||
config := &ssh.ServerConfig{
|
||||
@@ -262,6 +331,47 @@ func ExampleDial() {
|
||||
fmt.Println(b.String())
|
||||
}
|
||||
|
||||
func ExampleClientConfig() {
|
||||
var hostKey ssh.PublicKey
|
||||
key, err := os.ReadFile("/home/user/.ssh/id_rsa")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to read private key: %v", err)
|
||||
}
|
||||
|
||||
// Create the Signer for this private key.
|
||||
signer, err := ssh.ParsePrivateKey(key)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to parse private key: %v", err)
|
||||
}
|
||||
|
||||
// Minimal ClientConfig with SHA-1 algoritms disabled.
|
||||
// The algorithms returned by ssh.SupportedAlgorithms() are different from
|
||||
// the default ones and do not include algorithms that are considered
|
||||
// insecure, such as those using SHA-1, returned by
|
||||
// ssh.InsecureAlgorithms().
|
||||
algorithms := ssh.SupportedAlgorithms()
|
||||
config := &ssh.ClientConfig{
|
||||
Config: ssh.Config{
|
||||
KeyExchanges: algorithms.KeyExchanges,
|
||||
Ciphers: algorithms.Ciphers,
|
||||
MACs: algorithms.MACs,
|
||||
},
|
||||
User: "username",
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
HostKeyCallback: ssh.FixedHostKey(hostKey),
|
||||
// We should check that hostKey algorithm is inlcuded in
|
||||
// algorithms.HostKeys.
|
||||
HostKeyAlgorithms: algorithms.HostKeys,
|
||||
}
|
||||
client, err := ssh.Dial("tcp", "yourserver.com:22", config)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to dial: ", err)
|
||||
}
|
||||
defer client.Close()
|
||||
}
|
||||
|
||||
func ExamplePublicKeys() {
|
||||
var hostKey ssh.PublicKey
|
||||
// A public key may be used to authenticate against the remote
|
||||
|
||||
+64
-24
@@ -5,7 +5,6 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -25,6 +24,11 @@ const debugHandshake = false
|
||||
// quickly.
|
||||
const chanSize = 16
|
||||
|
||||
// maxPendingPackets sets the maximum number of packets to queue while waiting
|
||||
// for KEX to complete. This limits the total pending data to maxPendingPackets
|
||||
// * maxPacket bytes, which is ~16.8MB.
|
||||
const maxPendingPackets = 64
|
||||
|
||||
// keyingTransport is a packet based transport that supports key
|
||||
// changes. It need not be thread-safe. It should pass through
|
||||
// msgNewKeys in both directions.
|
||||
@@ -34,7 +38,7 @@ type keyingTransport interface {
|
||||
// prepareKeyChange sets up a key change. The key change for a
|
||||
// direction will be effected if a msgNewKeys message is sent
|
||||
// or received.
|
||||
prepareKeyChange(*algorithms, *kexResult) error
|
||||
prepareKeyChange(*NegotiatedAlgorithms, *kexResult) error
|
||||
|
||||
// setStrictMode sets the strict KEX mode, notably triggering
|
||||
// sequence number resets on sending or receiving msgNewKeys.
|
||||
@@ -73,13 +77,22 @@ type handshakeTransport struct {
|
||||
incoming chan []byte
|
||||
readError error
|
||||
|
||||
mu sync.Mutex
|
||||
writeError error
|
||||
sentInitPacket []byte
|
||||
sentInitMsg *kexInitMsg
|
||||
pendingPackets [][]byte // Used when a key exchange is in progress.
|
||||
mu sync.Mutex
|
||||
// Condition for the above mutex. It is used to notify a completed key
|
||||
// exchange or a write failure. Writes can wait for this condition while a
|
||||
// key exchange is in progress.
|
||||
writeCond *sync.Cond
|
||||
writeError error
|
||||
sentInitPacket []byte
|
||||
sentInitMsg *kexInitMsg
|
||||
// Used to queue writes when a key exchange is in progress. The length is
|
||||
// limited by pendingPacketsSize. Once full, writes will block until the key
|
||||
// exchange is completed or an error occurs. If not empty, it is emptied
|
||||
// all at once when the key exchange is completed in kexLoop.
|
||||
pendingPackets [][]byte
|
||||
writePacketsLeft uint32
|
||||
writeBytesLeft int64
|
||||
userAuthComplete bool // whether the user authentication phase is complete
|
||||
|
||||
// If the read loop wants to schedule a kex, it pings this
|
||||
// channel, and the write loop will send out a kex
|
||||
@@ -102,7 +115,7 @@ type handshakeTransport struct {
|
||||
bannerCallback BannerCallback
|
||||
|
||||
// Algorithms agreed in the last key exchange.
|
||||
algorithms *algorithms
|
||||
algorithms *NegotiatedAlgorithms
|
||||
|
||||
// Counters exclusively owned by readLoop.
|
||||
readPacketsLeft uint32
|
||||
@@ -133,6 +146,7 @@ func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion,
|
||||
|
||||
config: config,
|
||||
}
|
||||
t.writeCond = sync.NewCond(&t.mu)
|
||||
t.resetReadThresholds()
|
||||
t.resetWriteThresholds()
|
||||
|
||||
@@ -150,7 +164,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
||||
if config.HostKeyAlgorithms != nil {
|
||||
t.hostKeyAlgorithms = config.HostKeyAlgorithms
|
||||
} else {
|
||||
t.hostKeyAlgorithms = supportedHostKeyAlgos
|
||||
t.hostKeyAlgorithms = defaultHostKeyAlgos
|
||||
}
|
||||
go t.readLoop()
|
||||
go t.kexLoop()
|
||||
@@ -170,6 +184,10 @@ func (t *handshakeTransport) getSessionID() []byte {
|
||||
return t.sessionID
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) getAlgorithms() NegotiatedAlgorithms {
|
||||
return *t.algorithms
|
||||
}
|
||||
|
||||
// waitSession waits for the session to be established. This should be
|
||||
// the first thing to call after instantiating handshakeTransport.
|
||||
func (t *handshakeTransport) waitSession() error {
|
||||
@@ -259,6 +277,7 @@ func (t *handshakeTransport) recordWriteError(err error) {
|
||||
defer t.mu.Unlock()
|
||||
if t.writeError == nil && err != nil {
|
||||
t.writeError = err
|
||||
t.writeCond.Broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +294,7 @@ func (t *handshakeTransport) resetWriteThresholds() {
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.writeBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.writeBytesLeft = t.algorithms.w.rekeyBytes()
|
||||
t.writeBytesLeft = t.algorithms.Write.rekeyBytes()
|
||||
} else {
|
||||
t.writeBytesLeft = 1 << 30
|
||||
}
|
||||
@@ -362,6 +381,8 @@ write:
|
||||
}
|
||||
}
|
||||
t.pendingPackets = t.pendingPackets[:0]
|
||||
// Unblock writePacket if waiting for KEX.
|
||||
t.writeCond.Broadcast()
|
||||
t.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -390,7 +411,7 @@ func (t *handshakeTransport) resetReadThresholds() {
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.readBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.readBytesLeft = t.algorithms.r.rekeyBytes()
|
||||
t.readBytesLeft = t.algorithms.Read.rekeyBytes()
|
||||
} else {
|
||||
t.readBytesLeft = 1 << 30
|
||||
}
|
||||
@@ -483,7 +504,7 @@ func (t *handshakeTransport) sendKexInit() error {
|
||||
CompressionClientServer: supportedCompressions,
|
||||
CompressionServerClient: supportedCompressions,
|
||||
}
|
||||
io.ReadFull(rand.Reader, msg.Cookie[:])
|
||||
io.ReadFull(t.config.Rand, msg.Cookie[:])
|
||||
|
||||
// We mutate the KexAlgos slice, in order to add the kex-strict extension algorithm,
|
||||
// and possibly to add the ext-info extension algorithm. Since the slice may be the
|
||||
@@ -552,26 +573,44 @@ func (t *handshakeTransport) sendKexInit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errSendBannerPhase = errors.New("ssh: SendAuthBanner outside of authentication phase")
|
||||
|
||||
func (t *handshakeTransport) writePacket(p []byte) error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
switch p[0] {
|
||||
case msgKexInit:
|
||||
return errors.New("ssh: only handshakeTransport can send kexInit")
|
||||
case msgNewKeys:
|
||||
return errors.New("ssh: only handshakeTransport can send newKeys")
|
||||
case msgUserAuthBanner:
|
||||
if t.userAuthComplete {
|
||||
return errSendBannerPhase
|
||||
}
|
||||
case msgUserAuthSuccess:
|
||||
t.userAuthComplete = true
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.writeError != nil {
|
||||
return t.writeError
|
||||
}
|
||||
|
||||
if t.sentInitMsg != nil {
|
||||
// Copy the packet so the writer can reuse the buffer.
|
||||
cp := make([]byte, len(p))
|
||||
copy(cp, p)
|
||||
t.pendingPackets = append(t.pendingPackets, cp)
|
||||
return nil
|
||||
if len(t.pendingPackets) < maxPendingPackets {
|
||||
// Copy the packet so the writer can reuse the buffer.
|
||||
cp := make([]byte, len(p))
|
||||
copy(cp, p)
|
||||
t.pendingPackets = append(t.pendingPackets, cp)
|
||||
return nil
|
||||
}
|
||||
for t.sentInitMsg != nil {
|
||||
// Block and wait for KEX to complete or an error.
|
||||
t.writeCond.Wait()
|
||||
if t.writeError != nil {
|
||||
return t.writeError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t.writeBytesLeft > 0 {
|
||||
@@ -588,6 +627,7 @@ func (t *handshakeTransport) writePacket(p []byte) error {
|
||||
|
||||
if err := t.pushPacket(p); err != nil {
|
||||
t.writeError = err
|
||||
t.writeCond.Broadcast()
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -664,9 +704,9 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
}
|
||||
}
|
||||
|
||||
kex, ok := kexAlgoMap[t.algorithms.kex]
|
||||
kex, ok := kexAlgoMap[t.algorithms.KeyExchange]
|
||||
if !ok {
|
||||
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex)
|
||||
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.KeyExchange)
|
||||
}
|
||||
|
||||
var result *kexResult
|
||||
@@ -773,12 +813,12 @@ func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) server(kex kexAlgorithm, magics *handshakeMagics) (*kexResult, error) {
|
||||
hostKey := pickHostKey(t.hostKeys, t.algorithms.hostKey)
|
||||
hostKey := pickHostKey(t.hostKeys, t.algorithms.HostKey)
|
||||
if hostKey == nil {
|
||||
return nil, errors.New("ssh: internal error: negotiated unsupported signature type")
|
||||
}
|
||||
|
||||
r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.algorithms.hostKey)
|
||||
r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.algorithms.HostKey)
|
||||
return r, err
|
||||
}
|
||||
|
||||
@@ -793,7 +833,7 @@ func (t *handshakeTransport) client(kex kexAlgorithm, magics *handshakeMagics) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := verifyHostKeySignature(hostKey, t.algorithms.hostKey, result); err != nil {
|
||||
if err := verifyHostKeySignature(hostKey, t.algorithms.HostKey, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
+348
-3
@@ -367,7 +367,7 @@ type errorKeyingTransport struct {
|
||||
readLeft, writeLeft int
|
||||
}
|
||||
|
||||
func (n *errorKeyingTransport) prepareKeyChange(*algorithms, *kexResult) error {
|
||||
func (n *errorKeyingTransport) prepareKeyChange(*NegotiatedAlgorithms, *kexResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -539,10 +539,230 @@ func TestDisconnect(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type mockKeyingTransport struct {
|
||||
packetConn
|
||||
kexInitAllowed chan struct{}
|
||||
kexInitSent chan struct{}
|
||||
}
|
||||
|
||||
func (n *mockKeyingTransport) prepareKeyChange(*NegotiatedAlgorithms, *kexResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *mockKeyingTransport) writePacket(packet []byte) error {
|
||||
if packet[0] == msgKexInit {
|
||||
<-n.kexInitAllowed
|
||||
n.kexInitSent <- struct{}{}
|
||||
}
|
||||
return n.packetConn.writePacket(packet)
|
||||
}
|
||||
|
||||
func (n *mockKeyingTransport) readPacket() ([]byte, error) {
|
||||
return n.packetConn.readPacket()
|
||||
}
|
||||
|
||||
func (n *mockKeyingTransport) setStrictMode() error { return nil }
|
||||
|
||||
func (n *mockKeyingTransport) setInitialKEXDone() {}
|
||||
|
||||
func TestHandshakePendingPacketsWait(t *testing.T) {
|
||||
a, b := memPipe()
|
||||
|
||||
trS := &mockKeyingTransport{
|
||||
packetConn: a,
|
||||
kexInitAllowed: make(chan struct{}, 2),
|
||||
kexInitSent: make(chan struct{}, 2),
|
||||
}
|
||||
// Allow the first KEX.
|
||||
trS.kexInitAllowed <- struct{}{}
|
||||
|
||||
trC := &mockKeyingTransport{
|
||||
packetConn: b,
|
||||
kexInitAllowed: make(chan struct{}, 2),
|
||||
kexInitSent: make(chan struct{}, 2),
|
||||
}
|
||||
// Allow the first KEX.
|
||||
trC.kexInitAllowed <- struct{}{}
|
||||
|
||||
clientConf := &ClientConfig{
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
clientConf.SetDefaults()
|
||||
|
||||
v := []byte("version")
|
||||
client := newClientTransport(trC, v, v, clientConf, "addr", nil)
|
||||
|
||||
serverConf := &ServerConfig{}
|
||||
serverConf.AddHostKey(testSigners["ecdsa"])
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
serverConf.SetDefaults()
|
||||
server := newServerTransport(trS, v, v, serverConf)
|
||||
|
||||
if err := server.waitSession(); err != nil {
|
||||
t.Fatalf("server.waitSession: %v", err)
|
||||
}
|
||||
if err := client.waitSession(); err != nil {
|
||||
t.Fatalf("client.waitSession: %v", err)
|
||||
}
|
||||
|
||||
<-trC.kexInitSent
|
||||
<-trS.kexInitSent
|
||||
|
||||
// Allow and request new KEX server side.
|
||||
trS.kexInitAllowed <- struct{}{}
|
||||
server.requestKeyExchange()
|
||||
// Wait until the KEX init is sent.
|
||||
<-trS.kexInitSent
|
||||
// The client is not allowed to respond to the KEX, so writes will be
|
||||
// blocked on the server side once the packets queue is full.
|
||||
for i := 0; i < maxPendingPackets; i++ {
|
||||
p := []byte{msgRequestSuccess, byte(i)}
|
||||
if err := server.writePacket(p); err != nil {
|
||||
t.Errorf("unexpected write error: %v", err)
|
||||
}
|
||||
}
|
||||
// The packets queue is now full, the next write will block.
|
||||
server.mu.Lock()
|
||||
if len(server.pendingPackets) != maxPendingPackets {
|
||||
t.Errorf("unexpected pending packets size; got: %d, want: %d", len(server.pendingPackets), maxPendingPackets)
|
||||
}
|
||||
server.mu.Unlock()
|
||||
|
||||
writeDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(writeDone)
|
||||
|
||||
p := []byte{msgRequestSuccess, byte(65)}
|
||||
// This write will block until KEX completes.
|
||||
err := server.writePacket(p)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected write error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Consume packets on the client side
|
||||
readDone := make(chan bool)
|
||||
go func() {
|
||||
defer close(readDone)
|
||||
|
||||
for {
|
||||
if _, err := client.readPacket(); err != nil {
|
||||
if err != io.EOF {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Allow the client to reply to the KEX and so unblock the write goroutine.
|
||||
trC.kexInitAllowed <- struct{}{}
|
||||
<-trC.kexInitSent
|
||||
<-writeDone
|
||||
// Close the client to unblock the read goroutine.
|
||||
client.Close()
|
||||
<-readDone
|
||||
server.Close()
|
||||
}
|
||||
|
||||
func TestHandshakePendingPacketsError(t *testing.T) {
|
||||
a, b := memPipe()
|
||||
|
||||
trS := &mockKeyingTransport{
|
||||
packetConn: a,
|
||||
kexInitAllowed: make(chan struct{}, 2),
|
||||
kexInitSent: make(chan struct{}, 2),
|
||||
}
|
||||
// Allow the first KEX.
|
||||
trS.kexInitAllowed <- struct{}{}
|
||||
|
||||
trC := &mockKeyingTransport{
|
||||
packetConn: b,
|
||||
kexInitAllowed: make(chan struct{}, 2),
|
||||
kexInitSent: make(chan struct{}, 2),
|
||||
}
|
||||
// Allow the first KEX.
|
||||
trC.kexInitAllowed <- struct{}{}
|
||||
|
||||
clientConf := &ClientConfig{
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
clientConf.SetDefaults()
|
||||
|
||||
v := []byte("version")
|
||||
client := newClientTransport(trC, v, v, clientConf, "addr", nil)
|
||||
|
||||
serverConf := &ServerConfig{}
|
||||
serverConf.AddHostKey(testSigners["ecdsa"])
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
serverConf.SetDefaults()
|
||||
server := newServerTransport(trS, v, v, serverConf)
|
||||
|
||||
if err := server.waitSession(); err != nil {
|
||||
t.Fatalf("server.waitSession: %v", err)
|
||||
}
|
||||
if err := client.waitSession(); err != nil {
|
||||
t.Fatalf("client.waitSession: %v", err)
|
||||
}
|
||||
|
||||
<-trC.kexInitSent
|
||||
<-trS.kexInitSent
|
||||
|
||||
// Allow and request new KEX server side.
|
||||
trS.kexInitAllowed <- struct{}{}
|
||||
server.requestKeyExchange()
|
||||
// Wait until the KEX init is sent.
|
||||
<-trS.kexInitSent
|
||||
// The client is not allowed to respond to the KEX, so writes will be
|
||||
// blocked on the server side once the packets queue is full.
|
||||
for i := 0; i < maxPendingPackets; i++ {
|
||||
p := []byte{msgRequestSuccess, byte(i)}
|
||||
if err := server.writePacket(p); err != nil {
|
||||
t.Errorf("unexpected write error: %v", err)
|
||||
}
|
||||
}
|
||||
// The packets queue is now full, the next write will block.
|
||||
writeDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(writeDone)
|
||||
|
||||
p := []byte{msgRequestSuccess, byte(65)}
|
||||
// This write will block until KEX completes.
|
||||
err := server.writePacket(p)
|
||||
if err != io.EOF {
|
||||
t.Errorf("unexpected write error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Consume packets on the client side
|
||||
readDone := make(chan bool)
|
||||
go func() {
|
||||
defer close(readDone)
|
||||
|
||||
for {
|
||||
if _, err := client.readPacket(); err != nil {
|
||||
if err != io.EOF {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Close the server to unblock the write after an error
|
||||
server.Close()
|
||||
<-writeDone
|
||||
// Unblock the pending write and close the client to unblock the read
|
||||
// goroutine.
|
||||
trC.kexInitAllowed <- struct{}{}
|
||||
client.Close()
|
||||
<-readDone
|
||||
}
|
||||
|
||||
func TestHandshakeRekeyDefault(t *testing.T) {
|
||||
clientConf := &ClientConfig{
|
||||
Config: Config{
|
||||
Ciphers: []string{"aes128-ctr"},
|
||||
Ciphers: []string{CipherAES128CTR},
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
@@ -568,7 +788,7 @@ func TestHandshakeRekeyDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandshakeAEADCipherNoMAC(t *testing.T) {
|
||||
for _, cipher := range []string{chacha20Poly1305ID, gcm128CipherID} {
|
||||
for _, cipher := range []string{CipherChaCha20Poly1305, CipherAES128GCM} {
|
||||
checker := &syncChecker{
|
||||
called: make(chan int, 1),
|
||||
}
|
||||
@@ -1019,3 +1239,128 @@ func TestStrictKEXMixed(t *testing.T) {
|
||||
t.Fatalf("client.waitSession: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNegotiatedAlgorithms(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
var serverAlgorithms NegotiatedAlgorithms
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
if algorithmConn, ok := conn.(AlgorithmsConnMetadata); ok {
|
||||
serverAlgorithms = algorithmConn.Algorithms()
|
||||
return &Permissions{}, nil
|
||||
}
|
||||
return nil, errors.New("server conn does not implement AlgorithmsConnMetadata")
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
clientConf := &ClientConfig{
|
||||
User: "test",
|
||||
Auth: []AuthMethod{Password("testpw")},
|
||||
HostKeyCallback: FixedHostKey(testSigners["rsa"].PublicKey()),
|
||||
}
|
||||
|
||||
if conn, _, _, err := NewClientConn(c2, "", clientConf); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if algorithmConn, ok := conn.(AlgorithmsConnMetadata); ok {
|
||||
clientAlgorithms := algorithmConn.Algorithms()
|
||||
if clientAlgorithms.HostKey == "" {
|
||||
t.Fatal("negotiated client host key is empty")
|
||||
}
|
||||
if clientAlgorithms.KeyExchange == "" {
|
||||
t.Fatal("negotiated client KEX is empty")
|
||||
}
|
||||
if clientAlgorithms.Read.Cipher == "" {
|
||||
t.Fatal("negotiated client read cipher is empty")
|
||||
}
|
||||
if clientAlgorithms.Write.Cipher == "" {
|
||||
t.Fatal("negotiated client write cipher is empty")
|
||||
}
|
||||
if !reflect.DeepEqual(clientAlgorithms, serverAlgorithms) {
|
||||
t.Fatalf("negotiated client algorithms: %+v differs from negotiated server algorithms: %+v",
|
||||
clientAlgorithms, serverAlgorithms)
|
||||
}
|
||||
} else {
|
||||
t.Fatal("client conn does not implement AlgorithmsConnMetadata")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlgorithmNegotiationError(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
Config: Config{
|
||||
Ciphers: []string{CipherAES128CTR, CipherAES256CTR},
|
||||
},
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
return &Permissions{}, nil
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
srvErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
_, _, _, err := NewServerConn(c1, serverConf)
|
||||
srvErrCh <- err
|
||||
}()
|
||||
|
||||
clientConf := &ClientConfig{
|
||||
Config: Config{
|
||||
Ciphers: []string{CipherAES128GCM, CipherAES256GCM},
|
||||
},
|
||||
User: "test",
|
||||
Auth: []AuthMethod{Password("testpw")},
|
||||
HostKeyCallback: FixedHostKey(testSigners["rsa"].PublicKey()),
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||
if err == nil {
|
||||
t.Fatal("client connection succeded expected algorithm negotiation error")
|
||||
}
|
||||
var negotiationError *AlgorithmNegotiationError
|
||||
if !errors.As(err, &negotiationError) {
|
||||
t.Fatalf("expected algorithm negotiation error, got %v", err)
|
||||
}
|
||||
expectedErrorString := fmt.Sprintf("ssh: handshake failed: ssh: no common algorithm for client to server cipher; we offered: %v, peer offered: %v",
|
||||
clientConf.Ciphers, serverConf.Ciphers)
|
||||
if err.Error() != expectedErrorString {
|
||||
t.Fatalf("expected error string %q, got %q", expectedErrorString, err.Error())
|
||||
}
|
||||
if !reflect.DeepEqual(negotiationError.RequestedAlgorithms, serverConf.Ciphers) {
|
||||
t.Fatalf("expected requested algorithms %v, got %v", serverConf.Ciphers, negotiationError.RequestedAlgorithms)
|
||||
}
|
||||
if !reflect.DeepEqual(negotiationError.SupportedAlgorithms, clientConf.Ciphers) {
|
||||
t.Fatalf("expected supported algorithms %v, got %v", clientConf.Ciphers, negotiationError.SupportedAlgorithms)
|
||||
}
|
||||
err = <-srvErrCh
|
||||
negotiationError = nil
|
||||
if !errors.As(err, &negotiationError) {
|
||||
t.Fatalf("expected algorithm negotiation error, got %v", err)
|
||||
}
|
||||
expectedErrorString = fmt.Sprintf("ssh: no common algorithm for client to server cipher; we offered: %v, peer offered: %v",
|
||||
serverConf.Ciphers, clientConf.Ciphers)
|
||||
if err.Error() != expectedErrorString {
|
||||
t.Fatalf("expected error string %q, got %q", expectedErrorString, err.Error())
|
||||
}
|
||||
if !reflect.DeepEqual(negotiationError.RequestedAlgorithms, clientConf.Ciphers) {
|
||||
t.Fatalf("expected requested algorithms %v, got %v", clientConf.Ciphers, negotiationError.RequestedAlgorithms)
|
||||
}
|
||||
if !reflect.DeepEqual(negotiationError.SupportedAlgorithms, serverConf.Ciphers) {
|
||||
t.Fatalf("expected supported algorithms %v, got %v", serverConf.Ciphers, negotiationError.SupportedAlgorithms)
|
||||
}
|
||||
}
|
||||
|
||||
+72
-67
@@ -9,7 +9,6 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -20,21 +19,18 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
|
||||
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
|
||||
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
|
||||
kexAlgoDH16SHA512 = "diffie-hellman-group16-sha512"
|
||||
kexAlgoECDH256 = "ecdh-sha2-nistp256"
|
||||
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
||||
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
||||
kexAlgoCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
|
||||
kexAlgoCurve25519SHA256 = "curve25519-sha256"
|
||||
|
||||
// For the following kex only the client half contains a production
|
||||
// ready implementation. The server half only consists of a minimal
|
||||
// implementation to satisfy the automated tests.
|
||||
kexAlgoDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
|
||||
kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
|
||||
// This is the group called diffie-hellman-group1-sha1 in RFC 4253 and
|
||||
// Oakley Group 2 in RFC 2409.
|
||||
oakleyGroup2 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
|
||||
// This is the group called diffie-hellman-group14-sha1 in RFC 4253 and
|
||||
// Oakley Group 14 in RFC 3526.
|
||||
oakleyGroup14 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"
|
||||
// This is the group called diffie-hellman-group15-sha512 in RFC 8268 and
|
||||
// Oakley Group 15 in RFC 3526.
|
||||
oakleyGroup15 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
|
||||
// This is the group called diffie-hellman-group16-sha512 in RFC 8268 and
|
||||
// Oakley Group 16 in RFC 3526.
|
||||
oakleyGroup16 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF"
|
||||
)
|
||||
|
||||
// kexResult captures the outcome of a key exchange.
|
||||
@@ -402,53 +398,47 @@ func ecHash(curve elliptic.Curve) crypto.Hash {
|
||||
var kexAlgoMap = map[string]kexAlgorithm{}
|
||||
|
||||
func init() {
|
||||
// This is the group called diffie-hellman-group1-sha1 in
|
||||
// RFC 4253 and Oakley Group 2 in RFC 2409.
|
||||
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
|
||||
kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
|
||||
p, _ := new(big.Int).SetString(oakleyGroup2, 16)
|
||||
kexAlgoMap[InsecureKeyExchangeDH1SHA1] = &dhGroup{
|
||||
g: new(big.Int).SetInt64(2),
|
||||
p: p,
|
||||
pMinus1: new(big.Int).Sub(p, bigOne),
|
||||
hashFunc: crypto.SHA1,
|
||||
}
|
||||
|
||||
// This are the groups called diffie-hellman-group14-sha1 and
|
||||
// diffie-hellman-group14-sha256 in RFC 4253 and RFC 8268,
|
||||
// and Oakley Group 14 in RFC 3526.
|
||||
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
|
||||
p, _ = new(big.Int).SetString(oakleyGroup14, 16)
|
||||
group14 := &dhGroup{
|
||||
g: new(big.Int).SetInt64(2),
|
||||
p: p,
|
||||
pMinus1: new(big.Int).Sub(p, bigOne),
|
||||
}
|
||||
|
||||
kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
|
||||
kexAlgoMap[InsecureKeyExchangeDH14SHA1] = &dhGroup{
|
||||
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
|
||||
hashFunc: crypto.SHA1,
|
||||
}
|
||||
kexAlgoMap[kexAlgoDH14SHA256] = &dhGroup{
|
||||
kexAlgoMap[KeyExchangeDH14SHA256] = &dhGroup{
|
||||
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
|
||||
hashFunc: crypto.SHA256,
|
||||
}
|
||||
|
||||
// This is the group called diffie-hellman-group16-sha512 in RFC
|
||||
// 8268 and Oakley Group 16 in RFC 3526.
|
||||
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", 16)
|
||||
p, _ = new(big.Int).SetString(oakleyGroup16, 16)
|
||||
|
||||
kexAlgoMap[kexAlgoDH16SHA512] = &dhGroup{
|
||||
kexAlgoMap[KeyExchangeDH16SHA512] = &dhGroup{
|
||||
g: new(big.Int).SetInt64(2),
|
||||
p: p,
|
||||
pMinus1: new(big.Int).Sub(p, bigOne),
|
||||
hashFunc: crypto.SHA512,
|
||||
}
|
||||
|
||||
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
|
||||
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
|
||||
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
|
||||
kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
|
||||
kexAlgoMap[kexAlgoCurve25519SHA256LibSSH] = &curve25519sha256{}
|
||||
kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
|
||||
kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
|
||||
kexAlgoMap[KeyExchangeECDHP521] = &ecdh{elliptic.P521()}
|
||||
kexAlgoMap[KeyExchangeECDHP384] = &ecdh{elliptic.P384()}
|
||||
kexAlgoMap[KeyExchangeECDHP256] = &ecdh{elliptic.P256()}
|
||||
kexAlgoMap[KeyExchangeCurve25519] = &curve25519sha256{}
|
||||
kexAlgoMap[keyExchangeCurve25519LibSSH] = &curve25519sha256{}
|
||||
kexAlgoMap[InsecureKeyExchangeDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
|
||||
kexAlgoMap[KeyExchangeDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
|
||||
kexAlgoMap[KeyExchangeMLKEM768X25519] = &mlkem768WithCurve25519sha256{}
|
||||
}
|
||||
|
||||
// curve25519sha256 implements the curve25519-sha256 (formerly known as
|
||||
@@ -464,15 +454,17 @@ func (kp *curve25519KeyPair) generate(rand io.Reader) error {
|
||||
if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
|
||||
p, err := curve25519.X25519(kp.priv[:], curve25519.Basepoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("curve25519: %w", err)
|
||||
}
|
||||
if len(p) != 32 {
|
||||
return fmt.Errorf("curve25519: internal error: X25519 returned %d bytes, expected 32", len(p))
|
||||
}
|
||||
copy(kp.pub[:], p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// curve25519Zeros is just an array of 32 zero bytes so that we have something
|
||||
// convenient to compare against in order to reject curve25519 points with the
|
||||
// wrong order.
|
||||
var curve25519Zeros [32]byte
|
||||
|
||||
func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
||||
var kp curve25519KeyPair
|
||||
if err := kp.generate(rand); err != nil {
|
||||
@@ -495,11 +487,9 @@ func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handsh
|
||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
|
||||
}
|
||||
|
||||
var servPub, secret [32]byte
|
||||
copy(servPub[:], reply.EphemeralPubKey)
|
||||
curve25519.ScalarMult(&secret, &kp.priv, &servPub)
|
||||
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
|
||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
|
||||
secret, err := curve25519.X25519(kp.priv[:], reply.EphemeralPubKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh: peer's curve25519 public value is not valid: %w", err)
|
||||
}
|
||||
|
||||
h := crypto.SHA256.New()
|
||||
@@ -541,11 +531,9 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var clientPub, secret [32]byte
|
||||
copy(clientPub[:], kexInit.ClientPubKey)
|
||||
curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
|
||||
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
|
||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
|
||||
secret, err := curve25519.X25519(kp.priv[:], kexInit.ClientPubKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh: peer's curve25519 public value is not valid: %w", err)
|
||||
}
|
||||
|
||||
hostKeyBytes := priv.PublicKey().Marshal()
|
||||
@@ -601,9 +589,9 @@ const (
|
||||
func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
||||
// Send GexRequest
|
||||
kexDHGexRequest := kexDHGexRequestMsg{
|
||||
MinBits: dhGroupExchangeMinimumBits,
|
||||
PreferedBits: dhGroupExchangePreferredBits,
|
||||
MaxBits: dhGroupExchangeMaximumBits,
|
||||
MinBits: dhGroupExchangeMinimumBits,
|
||||
PreferredBits: dhGroupExchangePreferredBits,
|
||||
MaxBits: dhGroupExchangeMaximumBits,
|
||||
}
|
||||
if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil {
|
||||
return nil, err
|
||||
@@ -690,9 +678,7 @@ func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshak
|
||||
}
|
||||
|
||||
// Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256.
|
||||
//
|
||||
// This is a minimal implementation to satisfy the automated tests.
|
||||
func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) {
|
||||
func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) {
|
||||
// Receive GexRequest
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
@@ -702,13 +688,32 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
|
||||
if err = Unmarshal(packet, &kexDHGexRequest); err != nil {
|
||||
return
|
||||
}
|
||||
// We check that the request received is valid and that the MaxBits
|
||||
// requested are at least equal to our supported minimum. This is the same
|
||||
// check done in OpenSSH:
|
||||
// https://github.com/openssh/openssh-portable/blob/80a2f64b/kexgexs.c#L94
|
||||
//
|
||||
// Furthermore, we also check that the required MinBits are less than or
|
||||
// equal to 4096 because we can use up to Oakley Group 16.
|
||||
if kexDHGexRequest.MaxBits < kexDHGexRequest.MinBits || kexDHGexRequest.PreferredBits < kexDHGexRequest.MinBits ||
|
||||
kexDHGexRequest.MaxBits < kexDHGexRequest.PreferredBits || kexDHGexRequest.MaxBits < dhGroupExchangeMinimumBits ||
|
||||
kexDHGexRequest.MinBits > 4096 {
|
||||
return nil, fmt.Errorf("ssh: DH GEX request out of range, min: %d, max: %d, preferred: %d", kexDHGexRequest.MinBits,
|
||||
kexDHGexRequest.MaxBits, kexDHGexRequest.PreferredBits)
|
||||
}
|
||||
|
||||
var p *big.Int
|
||||
// We hardcode sending Oakley Group 14 (2048 bits), Oakley Group 15 (3072
|
||||
// bits) or Oakley Group 16 (4096 bits), based on the requested max size.
|
||||
if kexDHGexRequest.MaxBits < 3072 {
|
||||
p, _ = new(big.Int).SetString(oakleyGroup14, 16)
|
||||
} else if kexDHGexRequest.MaxBits < 4096 {
|
||||
p, _ = new(big.Int).SetString(oakleyGroup15, 16)
|
||||
} else {
|
||||
p, _ = new(big.Int).SetString(oakleyGroup16, 16)
|
||||
}
|
||||
|
||||
// Send GexGroup
|
||||
// This is the group called diffie-hellman-group14-sha1 in RFC
|
||||
// 4253 and Oakley Group 14 in RFC 3526.
|
||||
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
|
||||
g := big.NewInt(2)
|
||||
|
||||
msg := &kexDHGexGroupMsg{
|
||||
P: p,
|
||||
G: g,
|
||||
@@ -746,9 +751,9 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
|
||||
h := gex.hashFunc.New()
|
||||
magics.write(h)
|
||||
writeString(h, hostKeyBytes)
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
|
||||
binary.Write(h, binary.BigEndian, kexDHGexRequest.MinBits)
|
||||
binary.Write(h, binary.BigEndian, kexDHGexRequest.PreferredBits)
|
||||
binary.Write(h, binary.BigEndian, kexDHGexRequest.MaxBits)
|
||||
writeInt(h, p)
|
||||
writeInt(h, g)
|
||||
writeInt(h, kexDHGexInit.X)
|
||||
|
||||
+16
-11
@@ -36,14 +36,19 @@ import (
|
||||
// ClientConfig.HostKeyAlgorithms, Signature.Format, or as AlgorithmSigner
|
||||
// arguments.
|
||||
const (
|
||||
KeyAlgoRSA = "ssh-rsa"
|
||||
KeyAlgoDSA = "ssh-dss"
|
||||
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
|
||||
KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
|
||||
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
|
||||
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
|
||||
KeyAlgoED25519 = "ssh-ed25519"
|
||||
KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com"
|
||||
KeyAlgoRSA = "ssh-rsa"
|
||||
// Deprecated: DSA is only supported at insecure key sizes, and was removed
|
||||
// from major implementations.
|
||||
KeyAlgoDSA = InsecureKeyAlgoDSA
|
||||
// Deprecated: DSA is only supported at insecure key sizes, and was removed
|
||||
// from major implementations.
|
||||
InsecureKeyAlgoDSA = "ssh-dss"
|
||||
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
|
||||
KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
|
||||
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
|
||||
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
|
||||
KeyAlgoED25519 = "ssh-ed25519"
|
||||
KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com"
|
||||
|
||||
// KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms, not
|
||||
// public key formats, so they can't appear as a PublicKey.Type. The
|
||||
@@ -67,7 +72,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
|
||||
switch algo {
|
||||
case KeyAlgoRSA:
|
||||
return parseRSA(in)
|
||||
case KeyAlgoDSA:
|
||||
case InsecureKeyAlgoDSA:
|
||||
return parseDSA(in)
|
||||
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
||||
return parseECDSA(in)
|
||||
@@ -77,7 +82,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
|
||||
return parseED25519(in)
|
||||
case KeyAlgoSKED25519:
|
||||
return parseSKEd25519(in)
|
||||
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
|
||||
case CertAlgoRSAv01, InsecureCertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
|
||||
cert, err := parseCert(in, certKeyAlgoNames[algo])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -268,7 +273,7 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
|
||||
return nil, "", nil, nil, errors.New("ssh: no key found")
|
||||
}
|
||||
|
||||
// ParsePublicKey parses an SSH public key formatted for use in
|
||||
// ParsePublicKey parses an SSH public key or certificate formatted for use in
|
||||
// the SSH wire protocol according to RFC 4253, section 6.6.
|
||||
func ParsePublicKey(in []byte) (out PublicKey, err error) {
|
||||
algo, in, ok := parseString(in)
|
||||
|
||||
@@ -810,3 +810,30 @@ func TestCryptoPublicKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCertWithCertSignatureKey(t *testing.T) {
|
||||
certBytes := []byte(`-----BEGIN SSH CERTIFICATE-----
|
||||
AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPSp27hvNSB0
|
||||
IotJnVhjC4zxNgNS8BHlUCxD0VJi4D/eAAAAIIJMi1e5qfx+IFuKD/p/Ssqcb3os
|
||||
CpOw/4wBs1pQ53zwAAAAAAAAAAEAAAACAAAAAAAAABMAAAAPZm9vLmV4YW1wbGUu
|
||||
Y29tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0AAAAgc3NoLWVkMjU1
|
||||
MTktY2VydC12MDFAb3BlbnNzaC5jb20AAAAg+sNYhCO35mQT1UBMpmMk8ey+culd
|
||||
IU8vBlPEl4B07swAAAAggiv+RLnboS4znGCVl/n1jDg2uD0h15tW4s/04eS2mLQA
|
||||
AAAAAAAAAQAAAAIAAAAAAAAAEwAAAA9mb28uZXhhbXBsZS5jb20AAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACCV2wETgLKL
|
||||
Kt0bRl3YUnd/ZYSlq0xJMbn4Jj3cdPWykQAAAFMAAAALc3NoLWVkMjU1MTkAAABA
|
||||
WOdbRGEzyRAhiIK227CLUQD5caXYMV8FvSIB7toEE2M/8HnWdG9H3Rsg/v3unruQ
|
||||
JrQldnuPJNe7KOP2+zvUDgAAAFMAAAALc3NoLWVkMjU1MTkAAABAm3bIPp85ZpIe
|
||||
D+izJcUqlcAOri7HO8bULFNHT6LVegvB06xQ5TLwMlrxWUF4cafl1tSe8JQck4a6
|
||||
cLYUOHfQDw==
|
||||
-----END SSH CERTIFICATE-----
|
||||
`)
|
||||
block, _ := pem.Decode(certBytes)
|
||||
if block == nil {
|
||||
t.Fatal("invalid test certificate")
|
||||
}
|
||||
|
||||
if _, err := ParsePublicKey(block.Bytes); err == nil {
|
||||
t.Fatal("parsing an SSH certificate using another certificate as signature key succeeded; expected failure")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,8 +302,8 @@ func (k *KnownKey) String() string {
|
||||
// applications can offer an interactive prompt to the user.
|
||||
type KeyError struct {
|
||||
// Want holds the accepted host keys. For each key algorithm,
|
||||
// there can be one hostkey. If Want is empty, the host is
|
||||
// unknown. If Want is non-empty, there was a mismatch, which
|
||||
// there can be multiple hostkeys. If Want is empty, the host
|
||||
// is unknown. If Want is non-empty, there was a mismatch, which
|
||||
// can signify a MITM attack.
|
||||
Want []KnownKey
|
||||
}
|
||||
@@ -358,34 +358,20 @@ func (db *hostKeyDB) checkAddr(a addr, remoteKey ssh.PublicKey) error {
|
||||
// is just a key for the IP address, but not for the
|
||||
// hostname?
|
||||
|
||||
// Algorithm => key.
|
||||
knownKeys := map[string]KnownKey{}
|
||||
keyErr := &KeyError{}
|
||||
|
||||
for _, l := range db.lines {
|
||||
if l.match(a) {
|
||||
typ := l.knownKey.Key.Type()
|
||||
if _, ok := knownKeys[typ]; !ok {
|
||||
knownKeys[typ] = l.knownKey
|
||||
}
|
||||
if !l.match(a) {
|
||||
continue
|
||||
}
|
||||
|
||||
keyErr.Want = append(keyErr.Want, l.knownKey)
|
||||
if keyEq(l.knownKey.Key, remoteKey) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
keyErr := &KeyError{}
|
||||
for _, v := range knownKeys {
|
||||
keyErr.Want = append(keyErr.Want, v)
|
||||
}
|
||||
|
||||
// Unknown remote host.
|
||||
if len(knownKeys) == 0 {
|
||||
return keyErr
|
||||
}
|
||||
|
||||
// If the remote host starts using a different, unknown key type, we
|
||||
// also interpret that as a mismatch.
|
||||
if known, ok := knownKeys[remoteKey.Type()]; !ok || !keyEq(known.Key, remoteKey) {
|
||||
return keyErr
|
||||
}
|
||||
|
||||
return nil
|
||||
return keyErr
|
||||
}
|
||||
|
||||
// The Read function parses file contents.
|
||||
@@ -435,20 +421,26 @@ func New(files ...string) (ssh.HostKeyCallback, error) {
|
||||
return certChecker.CheckHostKey, nil
|
||||
}
|
||||
|
||||
// Normalize normalizes an address into the form used in known_hosts
|
||||
// Normalize normalizes an address into the form used in known_hosts. Supports
|
||||
// IPv4, hostnames, bracketed IPv6. Any other non-standard formats are returned
|
||||
// with minimal transformation.
|
||||
func Normalize(address string) string {
|
||||
const defaultSSHPort = "22"
|
||||
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
host = address
|
||||
port = "22"
|
||||
port = defaultSSHPort
|
||||
}
|
||||
entry := host
|
||||
if port != "22" {
|
||||
entry = "[" + entry + "]:" + port
|
||||
} else if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") {
|
||||
entry = "[" + entry + "]"
|
||||
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
host = host[1 : len(host)-1]
|
||||
}
|
||||
return entry
|
||||
|
||||
if port == defaultSSHPort {
|
||||
return host
|
||||
}
|
||||
return "[" + host + "]:" + port
|
||||
}
|
||||
|
||||
// Line returns a line to add append to the known_hosts files.
|
||||
|
||||
@@ -201,17 +201,6 @@ func TestHostNamePrecedence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBOrderingPrecedenceKeyType(t *testing.T) {
|
||||
str := fmt.Sprintf("server.org,%s %s\nserver.org,%s %s", testAddr, edKeyStr, testAddr, alternateEdKeyStr)
|
||||
db := testDB(t, str)
|
||||
|
||||
if err := db.check("server.org:22", testAddr, alternateEdKey); err == nil {
|
||||
t.Errorf("check succeeded")
|
||||
} else if _, ok := err.(*KeyError); !ok {
|
||||
t.Errorf("got %T, want *KeyError", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNegate(t *testing.T) {
|
||||
str := fmt.Sprintf("%s,!server.org %s", testAddr, edKeyStr)
|
||||
db := testDB(t, str)
|
||||
@@ -247,7 +236,7 @@ func TestLine(t *testing.T) {
|
||||
"server.org": "server.org " + edKeyStr,
|
||||
"server.org:22": "server.org " + edKeyStr,
|
||||
"server.org:23": "[server.org]:23 " + edKeyStr,
|
||||
"[c629:1ec4:102:304:102:304:102:304]:22": "[c629:1ec4:102:304:102:304:102:304] " + edKeyStr,
|
||||
"[c629:1ec4:102:304:102:304:102:304]:22": "c629:1ec4:102:304:102:304:102:304 " + edKeyStr,
|
||||
"[c629:1ec4:102:304:102:304:102:304]:23": "[c629:1ec4:102:304:102:304:102:304]:23 " + edKeyStr,
|
||||
} {
|
||||
if got := Line([]string{in}, edKey); got != want {
|
||||
@@ -321,14 +310,25 @@ func testHostHash(t *testing.T, hostname, encoded string) {
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
for in, want := range map[string]string{
|
||||
"127.0.0.1:22": "127.0.0.1",
|
||||
"[127.0.0.1]:22": "127.0.0.1",
|
||||
"[127.0.0.1]:23": "[127.0.0.1]:23",
|
||||
"127.0.0.1:23": "[127.0.0.1]:23",
|
||||
"[a.b.c]:22": "a.b.c",
|
||||
"[abcd:abcd:abcd:abcd]": "[abcd:abcd:abcd:abcd]",
|
||||
"[abcd:abcd:abcd:abcd]:22": "[abcd:abcd:abcd:abcd]",
|
||||
"[abcd:abcd:abcd:abcd]:23": "[abcd:abcd:abcd:abcd]:23",
|
||||
"127.0.0.1": "127.0.0.1",
|
||||
"127.0.0.1:22": "127.0.0.1",
|
||||
"[127.0.0.1]:22": "127.0.0.1",
|
||||
"[127.0.0.1]:23": "[127.0.0.1]:23",
|
||||
"127.0.0.1:23": "[127.0.0.1]:23",
|
||||
"[a.b.c]:22": "a.b.c",
|
||||
"[a.b.c]:23": "[a.b.c]:23",
|
||||
"abcd::abcd:abcd:abcd": "abcd::abcd:abcd:abcd",
|
||||
"[abcd::abcd:abcd:abcd]": "abcd::abcd:abcd:abcd",
|
||||
"[abcd::abcd:abcd:abcd]:22": "abcd::abcd:abcd:abcd",
|
||||
"[abcd::abcd:abcd:abcd]:23": "[abcd::abcd:abcd:abcd]:23",
|
||||
"2001:db8::1": "2001:db8::1",
|
||||
"2001:db8::1:22": "2001:db8::1:22",
|
||||
"[2001:db8::1]:22": "2001:db8::1",
|
||||
"2001:db8::1:2200": "2001:db8::1:2200",
|
||||
"a.b.c.d.com:2200": "[a.b.c.d.com]:2200",
|
||||
"2001::db8:1": "2001::db8:1",
|
||||
"2001::db8:1:22": "2001::db8:1:22",
|
||||
"2001::db8:1:2200": "2001::db8:1:2200",
|
||||
} {
|
||||
got := Normalize(in)
|
||||
if got != want {
|
||||
@@ -354,3 +354,16 @@ func TestHashedHostkeyCheck(t *testing.T) {
|
||||
t.Errorf("got error %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue36126(t *testing.T) {
|
||||
str := fmt.Sprintf("server.org,%s %s\nserver.org,%s %s", testAddr, edKeyStr, testAddr, alternateEdKeyStr)
|
||||
db := testDB(t, str)
|
||||
|
||||
if err := db.check("server.org:22", testAddr, edKey); err != nil {
|
||||
t.Errorf("should have passed the check, got %v", err)
|
||||
}
|
||||
|
||||
if err := db.check("server.org:22", testAddr, alternateEdKey); err != nil {
|
||||
t.Errorf("should have passed the check, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -47,22 +47,22 @@ func (t truncatingMAC) Size() int {
|
||||
func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
|
||||
|
||||
var macModes = map[string]*macMode{
|
||||
"hmac-sha2-512-etm@openssh.com": {64, true, func(key []byte) hash.Hash {
|
||||
HMACSHA512ETM: {64, true, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha512.New, key)
|
||||
}},
|
||||
"hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
|
||||
HMACSHA256ETM: {32, true, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}},
|
||||
"hmac-sha2-512": {64, false, func(key []byte) hash.Hash {
|
||||
HMACSHA512: {64, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha512.New, key)
|
||||
}},
|
||||
"hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
|
||||
HMACSHA256: {32, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}},
|
||||
"hmac-sha1": {20, false, func(key []byte) hash.Hash {
|
||||
HMACSHA1: {20, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha1.New, key)
|
||||
}},
|
||||
"hmac-sha1-96": {20, false, func(key []byte) hash.Hash {
|
||||
InsecureHMACSHA196: {20, false, func(key []byte) hash.Hash {
|
||||
return truncatingMAC{12, hmac.New(sha1.New, key)}
|
||||
}},
|
||||
}
|
||||
|
||||
+5
-3
@@ -122,9 +122,9 @@ type kexDHGexReplyMsg struct {
|
||||
const msgKexDHGexRequest = 34
|
||||
|
||||
type kexDHGexRequestMsg struct {
|
||||
MinBits uint32 `sshtype:"34"`
|
||||
PreferedBits uint32
|
||||
MaxBits uint32
|
||||
MinBits uint32 `sshtype:"34"`
|
||||
PreferredBits uint32
|
||||
MaxBits uint32
|
||||
}
|
||||
|
||||
// See RFC 4253, section 10.
|
||||
@@ -818,6 +818,8 @@ func decode(packet []byte) (interface{}, error) {
|
||||
return new(userAuthSuccessMsg), nil
|
||||
case msgUserAuthFailure:
|
||||
msg = new(userAuthFailureMsg)
|
||||
case msgUserAuthBanner:
|
||||
msg = new(userAuthBannerMsg)
|
||||
case msgUserAuthPubKeyOk:
|
||||
msg = new(userAuthPubKeyOkMsg)
|
||||
case msgGlobalRequest:
|
||||
|
||||
@@ -206,6 +206,62 @@ func TestMarshalMultiTag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(0))
|
||||
kexInit := new(kexInitMsg).Generate(rnd, 10).Interface()
|
||||
kexDHInit := new(kexDHInitMsg).Generate(rnd, 10).Interface()
|
||||
kexDHReply := new(kexDHReplyMsg)
|
||||
kexDHReply.Y = randomInt(rnd)
|
||||
// Note: userAuthSuccessMsg can't be tested directly since it
|
||||
// doesn't have a field for sshtype. So it's tested separately
|
||||
// at the end.
|
||||
decodeMessageTypes := []interface{}{
|
||||
new(disconnectMsg),
|
||||
new(serviceRequestMsg),
|
||||
new(serviceAcceptMsg),
|
||||
new(extInfoMsg),
|
||||
kexInit,
|
||||
kexDHInit,
|
||||
kexDHReply,
|
||||
new(userAuthRequestMsg),
|
||||
new(userAuthFailureMsg),
|
||||
new(userAuthBannerMsg),
|
||||
new(userAuthPubKeyOkMsg),
|
||||
new(globalRequestMsg),
|
||||
new(globalRequestSuccessMsg),
|
||||
new(globalRequestFailureMsg),
|
||||
new(channelOpenMsg),
|
||||
new(channelDataMsg),
|
||||
new(channelOpenConfirmMsg),
|
||||
new(channelOpenFailureMsg),
|
||||
new(windowAdjustMsg),
|
||||
new(channelEOFMsg),
|
||||
new(channelCloseMsg),
|
||||
new(channelRequestMsg),
|
||||
new(channelRequestSuccessMsg),
|
||||
new(channelRequestFailureMsg),
|
||||
new(userAuthGSSAPIToken),
|
||||
new(userAuthGSSAPIMIC),
|
||||
new(userAuthGSSAPIErrTok),
|
||||
new(userAuthGSSAPIError),
|
||||
}
|
||||
for _, msg := range decodeMessageTypes {
|
||||
decoded, err := decode(Marshal(msg))
|
||||
if err != nil {
|
||||
t.Errorf("error decoding %T", msg)
|
||||
} else if reflect.TypeOf(msg) != reflect.TypeOf(decoded) {
|
||||
t.Errorf("error decoding %T, unexpected %T", msg, decoded)
|
||||
}
|
||||
}
|
||||
|
||||
userAuthSuccess, err := decode([]byte{msgUserAuthSuccess})
|
||||
if err != nil {
|
||||
t.Errorf("error decoding userAuthSuccessMsg")
|
||||
} else if reflect.TypeOf(userAuthSuccess) != reflect.TypeOf((*userAuthSuccessMsg)(nil)) {
|
||||
t.Errorf("error decoding userAuthSuccessMsg, unexpected %T", userAuthSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
func randomBytes(out []byte, rand *rand.Rand) {
|
||||
for i := 0; i < len(out); i++ {
|
||||
out[i] = byte(rand.Int31())
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
// Copyright 2024 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 ssh
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/mlkem"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
// mlkem768WithCurve25519sha256 implements the hybrid ML-KEM768 with
|
||||
// curve25519-sha256 key exchange method, as described by
|
||||
// draft-kampanakis-curdle-ssh-pq-ke-05 section 2.3.3.
|
||||
type mlkem768WithCurve25519sha256 struct{}
|
||||
|
||||
func (kex *mlkem768WithCurve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
||||
var c25519kp curve25519KeyPair
|
||||
if err := c25519kp.generate(rand); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seed := make([]byte, mlkem.SeedSize)
|
||||
if _, err := io.ReadFull(rand, seed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mlkemDk, err := mlkem.NewDecapsulationKey768(seed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hybridKey := append(mlkemDk.EncapsulationKey().Bytes(), c25519kp.pub[:]...)
|
||||
if err := c.writePacket(Marshal(&kexECDHInitMsg{hybridKey})); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply kexECDHReplyMsg
|
||||
if err = Unmarshal(packet, &reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(reply.EphemeralPubKey) != mlkem.CiphertextSize768+32 {
|
||||
return nil, errors.New("ssh: peer's mlkem768x25519 public value has wrong length")
|
||||
}
|
||||
|
||||
// Perform KEM decapsulate operation to obtain shared key from ML-KEM.
|
||||
mlkem768Secret, err := mlkemDk.Decapsulate(reply.EphemeralPubKey[:mlkem.CiphertextSize768])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Complete Curve25519 ECDH to obtain its shared key.
|
||||
c25519Secret, err := curve25519.X25519(c25519kp.priv[:], reply.EphemeralPubKey[mlkem.CiphertextSize768:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh: peer's mlkem768x25519 public value is not valid: %w", err)
|
||||
}
|
||||
// Compute actual shared key.
|
||||
h := sha256.New()
|
||||
h.Write(mlkem768Secret)
|
||||
h.Write(c25519Secret)
|
||||
secret := h.Sum(nil)
|
||||
|
||||
h.Reset()
|
||||
magics.write(h)
|
||||
writeString(h, reply.HostKey)
|
||||
writeString(h, hybridKey)
|
||||
writeString(h, reply.EphemeralPubKey)
|
||||
|
||||
K := make([]byte, stringLength(len(secret)))
|
||||
marshalString(K, secret)
|
||||
h.Write(K)
|
||||
|
||||
return &kexResult{
|
||||
H: h.Sum(nil),
|
||||
K: K,
|
||||
HostKey: reply.HostKey,
|
||||
Signature: reply.Signature,
|
||||
Hash: crypto.SHA256,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (kex *mlkem768WithCurve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (*kexResult, error) {
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kexInit kexECDHInitMsg
|
||||
if err = Unmarshal(packet, &kexInit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(kexInit.ClientPubKey) != mlkem.EncapsulationKeySize768+32 {
|
||||
return nil, errors.New("ssh: peer's ML-KEM768/curve25519 public value has wrong length")
|
||||
}
|
||||
|
||||
encapsulationKey, err := mlkem.NewEncapsulationKey768(kexInit.ClientPubKey[:mlkem.EncapsulationKeySize768])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh: peer's ML-KEM768 encapsulation key is not valid: %w", err)
|
||||
}
|
||||
// Perform KEM encapsulate operation to obtain ciphertext and shared key.
|
||||
mlkem768Secret, mlkem768Ciphertext := encapsulationKey.Encapsulate()
|
||||
|
||||
// Perform server side of Curve25519 ECDH to obtain server public value and
|
||||
// shared key.
|
||||
var c25519kp curve25519KeyPair
|
||||
if err := c25519kp.generate(rand); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c25519Secret, err := curve25519.X25519(c25519kp.priv[:], kexInit.ClientPubKey[mlkem.EncapsulationKeySize768:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh: peer's ML-KEM768/curve25519 public value is not valid: %w", err)
|
||||
}
|
||||
hybridKey := append(mlkem768Ciphertext, c25519kp.pub[:]...)
|
||||
|
||||
// Compute actual shared key.
|
||||
h := sha256.New()
|
||||
h.Write(mlkem768Secret)
|
||||
h.Write(c25519Secret)
|
||||
secret := h.Sum(nil)
|
||||
|
||||
hostKeyBytes := priv.PublicKey().Marshal()
|
||||
|
||||
h.Reset()
|
||||
magics.write(h)
|
||||
writeString(h, hostKeyBytes)
|
||||
writeString(h, kexInit.ClientPubKey)
|
||||
writeString(h, hybridKey)
|
||||
|
||||
K := make([]byte, stringLength(len(secret)))
|
||||
marshalString(K, secret)
|
||||
h.Write(K)
|
||||
|
||||
H := h.Sum(nil)
|
||||
|
||||
sig, err := signAndMarshal(priv, rand, H, algo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply := kexECDHReplyMsg{
|
||||
EphemeralPubKey: hybridKey,
|
||||
HostKey: hostKeyBytes,
|
||||
Signature: sig,
|
||||
}
|
||||
if err := c.writePacket(Marshal(&reply)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &kexResult{
|
||||
H: H,
|
||||
K: K,
|
||||
HostKey: hostKeyBytes,
|
||||
Signature: sig,
|
||||
Hash: crypto.SHA256,
|
||||
}, nil
|
||||
}
|
||||
+53
-28
@@ -59,6 +59,27 @@ type GSSAPIWithMICConfig struct {
|
||||
Server GSSAPIServer
|
||||
}
|
||||
|
||||
// SendAuthBanner implements [ServerPreAuthConn].
|
||||
func (s *connection) SendAuthBanner(msg string) error {
|
||||
return s.transport.writePacket(Marshal(&userAuthBannerMsg{
|
||||
Message: msg,
|
||||
}))
|
||||
}
|
||||
|
||||
func (*connection) unexportedMethodForFutureProofing() {}
|
||||
|
||||
// ServerPreAuthConn is the interface available on an incoming server
|
||||
// connection before authentication has completed.
|
||||
type ServerPreAuthConn interface {
|
||||
unexportedMethodForFutureProofing() // permits growing ServerPreAuthConn safely later, ala testing.TB
|
||||
|
||||
ConnMetadata
|
||||
|
||||
// SendAuthBanner sends a banner message to the client.
|
||||
// It returns an error once the authentication phase has ended.
|
||||
SendAuthBanner(string) error
|
||||
}
|
||||
|
||||
// ServerConfig holds server specific configuration data.
|
||||
type ServerConfig struct {
|
||||
// Config contains configuration shared between client and server.
|
||||
@@ -118,6 +139,12 @@ type ServerConfig struct {
|
||||
// attempts.
|
||||
AuthLogCallback func(conn ConnMetadata, method string, err error)
|
||||
|
||||
// PreAuthConnCallback, if non-nil, is called upon receiving a new connection
|
||||
// before any authentication has started. The provided ServerPreAuthConn
|
||||
// can be used at any time before authentication is complete, including
|
||||
// after this callback has returned.
|
||||
PreAuthConnCallback func(ServerPreAuthConn)
|
||||
|
||||
// ServerVersion is the version identification string to announce in
|
||||
// the public handshake.
|
||||
// If empty, a reasonable default is used.
|
||||
@@ -149,7 +176,7 @@ func (s *ServerConfig) AddHostKey(key Signer) {
|
||||
}
|
||||
|
||||
// cachedPubKey contains the results of querying whether a public key is
|
||||
// acceptable for a user.
|
||||
// acceptable for a user. This is a FIFO cache.
|
||||
type cachedPubKey struct {
|
||||
user string
|
||||
pubKeyData []byte
|
||||
@@ -157,7 +184,13 @@ type cachedPubKey struct {
|
||||
perms *Permissions
|
||||
}
|
||||
|
||||
const maxCachedPubKeys = 16
|
||||
// maxCachedPubKeys is the number of cache entries we store.
|
||||
//
|
||||
// Due to consistent misuse of the PublicKeyCallback API, we have reduced this
|
||||
// to 1, such that the only key in the cache is the most recently seen one. This
|
||||
// forces the behavior that the last call to PublicKeyCallback will always be
|
||||
// with the key that is used for authentication.
|
||||
const maxCachedPubKeys = 1
|
||||
|
||||
// pubKeyCache caches tests for public keys. Since SSH clients
|
||||
// will query whether a public key is acceptable before attempting to
|
||||
@@ -179,9 +212,10 @@ func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
|
||||
|
||||
// add adds the given tuple to the cache.
|
||||
func (c *pubKeyCache) add(candidate cachedPubKey) {
|
||||
if len(c.keys) < maxCachedPubKeys {
|
||||
c.keys = append(c.keys, candidate)
|
||||
if len(c.keys) >= maxCachedPubKeys {
|
||||
c.keys = c.keys[1:]
|
||||
}
|
||||
c.keys = append(c.keys, candidate)
|
||||
}
|
||||
|
||||
// ServerConn is an authenticated SSH connection, as seen from the
|
||||
@@ -209,22 +243,15 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
|
||||
fullConf.MaxAuthTries = 6
|
||||
}
|
||||
if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
|
||||
fullConf.PublicKeyAuthAlgorithms = supportedPubKeyAuthAlgos
|
||||
fullConf.PublicKeyAuthAlgorithms = defaultPubKeyAuthAlgos
|
||||
} else {
|
||||
for _, algo := range fullConf.PublicKeyAuthAlgorithms {
|
||||
if !contains(supportedPubKeyAuthAlgos, algo) {
|
||||
if !contains(SupportedAlgorithms().PublicKeyAuths, algo) && !contains(InsecureAlgorithms().PublicKeyAuths, algo) {
|
||||
c.Close()
|
||||
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if the config contains any unsupported key exchanges
|
||||
for _, kex := range fullConf.KeyExchanges {
|
||||
if _, ok := serverForbiddenKexAlgos[kex]; ok {
|
||||
c.Close()
|
||||
return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
|
||||
}
|
||||
}
|
||||
|
||||
s := &connection{
|
||||
sshConn: sshConn{conn: c},
|
||||
@@ -281,6 +308,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
||||
|
||||
// We just did the key change, so the session ID is established.
|
||||
s.sessionID = s.transport.getSessionID()
|
||||
s.algorithms = s.transport.getAlgorithms()
|
||||
|
||||
var packet []byte
|
||||
if packet, err = s.transport.readPacket(); err != nil {
|
||||
@@ -481,6 +509,10 @@ func (b *BannerError) Error() string {
|
||||
}
|
||||
|
||||
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
|
||||
if config.PreAuthConnCallback != nil {
|
||||
config.PreAuthConnCallback(s)
|
||||
}
|
||||
|
||||
sessionID := s.transport.getSessionID()
|
||||
var cache pubKeyCache
|
||||
var perms *Permissions
|
||||
@@ -488,7 +520,7 @@ func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, err
|
||||
authFailures := 0
|
||||
noneAuthCount := 0
|
||||
var authErrs []error
|
||||
var displayedBanner bool
|
||||
var calledBannerCallback bool
|
||||
partialSuccessReturned := false
|
||||
// Set the initial authentication callbacks from the config. They can be
|
||||
// changed if a PartialSuccessError is returned.
|
||||
@@ -510,8 +542,8 @@ userAuthLoop:
|
||||
if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, discMsg
|
||||
authErrs = append(authErrs, discMsg)
|
||||
return nil, &ServerAuthError{Errors: authErrs}
|
||||
}
|
||||
|
||||
var userAuthReq userAuthRequestMsg
|
||||
@@ -535,14 +567,10 @@ userAuthLoop:
|
||||
|
||||
s.user = userAuthReq.User
|
||||
|
||||
if !displayedBanner && config.BannerCallback != nil {
|
||||
displayedBanner = true
|
||||
msg := config.BannerCallback(s)
|
||||
if msg != "" {
|
||||
bannerMsg := &userAuthBannerMsg{
|
||||
Message: msg,
|
||||
}
|
||||
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
|
||||
if !calledBannerCallback && config.BannerCallback != nil {
|
||||
calledBannerCallback = true
|
||||
if msg := config.BannerCallback(s); msg != "" {
|
||||
if err := s.SendAuthBanner(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -755,10 +783,7 @@ userAuthLoop:
|
||||
var bannerErr *BannerError
|
||||
if errors.As(authErr, &bannerErr) {
|
||||
if bannerErr.Message != "" {
|
||||
bannerMsg := &userAuthBannerMsg{
|
||||
Message: bannerErr.Message,
|
||||
}
|
||||
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
|
||||
if err := s.SendAuthBanner(bannerErr.Message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
+136
-1
@@ -5,6 +5,7 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -211,7 +212,7 @@ func TestNewServerConnValidationErrors(t *testing.T) {
|
||||
|
||||
serverConf = &ServerConfig{
|
||||
Config: Config{
|
||||
KeyExchanges: []string{kexAlgoDHGEXSHA256},
|
||||
KeyExchanges: []string{KeyExchangeDHGEXSHA256},
|
||||
},
|
||||
}
|
||||
c = &markerConn{}
|
||||
@@ -299,6 +300,140 @@ func TestBannerError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKeyCallbackLastSeen(t *testing.T) {
|
||||
var lastSeenKey PublicKey
|
||||
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
serverConf := &ServerConfig{
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
lastSeenKey = key
|
||||
fmt.Printf("seen %#v\n", key)
|
||||
if _, ok := key.(*dsaPublicKey); !ok {
|
||||
return nil, errors.New("nope")
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["ecdsap256"])
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
NewServerConn(c1, serverConf)
|
||||
}()
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"], testSigners["dsa"], testSigners["ed25519"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
expectedPublicKey := testSigners["dsa"].PublicKey().Marshal()
|
||||
lastSeenMarshalled := lastSeenKey.Marshal()
|
||||
if !bytes.Equal(lastSeenMarshalled, expectedPublicKey) {
|
||||
t.Errorf("unexpected key: got %#v, want %#v", lastSeenKey, testSigners["dsa"].PublicKey())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreAuthConnAndBanners(t *testing.T) {
|
||||
testDone := make(chan struct{})
|
||||
defer close(testDone)
|
||||
|
||||
authConnc := make(chan ServerPreAuthConn, 1)
|
||||
serverConfig := &ServerConfig{
|
||||
PreAuthConnCallback: func(c ServerPreAuthConn) {
|
||||
t.Logf("got ServerPreAuthConn: %v", c)
|
||||
authConnc <- c // for use later in the test
|
||||
for _, s := range []string{"hello1", "hello2"} {
|
||||
if err := c.SendAuthBanner(s); err != nil {
|
||||
t.Errorf("failed to send banner %q: %v", s, err)
|
||||
}
|
||||
}
|
||||
// Now start a goroutine to spam SendAuthBanner in hopes
|
||||
// of hitting a race.
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-testDone:
|
||||
return
|
||||
default:
|
||||
if err := c.SendAuthBanner("attempted-race"); err != nil && err != errSendBannerPhase {
|
||||
t.Errorf("unexpected error from SendAuthBanner: %v", err)
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}()
|
||||
},
|
||||
NoClientAuth: true,
|
||||
NoClientAuthCallback: func(ConnMetadata) (*Permissions, error) {
|
||||
t.Logf("got NoClientAuthCallback")
|
||||
return &Permissions{}, nil
|
||||
},
|
||||
}
|
||||
serverConfig.AddHostKey(testSigners["rsa"])
|
||||
|
||||
var banners []string
|
||||
clientConfig := &ClientConfig{
|
||||
User: "test",
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
BannerCallback: func(msg string) error {
|
||||
if msg != "attempted-race" {
|
||||
banners = append(banners, msg)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
go newServer(c1, serverConfig)
|
||||
c, _, _, err := NewClientConn(c2, "", clientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("client connection failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
wantBanners := []string{
|
||||
"hello1",
|
||||
"hello2",
|
||||
}
|
||||
if !reflect.DeepEqual(banners, wantBanners) {
|
||||
t.Errorf("got banners:\n%q\nwant banners:\n%q", banners, wantBanners)
|
||||
}
|
||||
|
||||
// Now that we're authenticated, verify that use of SendBanner
|
||||
// is an error.
|
||||
var bc ServerPreAuthConn
|
||||
select {
|
||||
case bc = <-authConnc:
|
||||
default:
|
||||
t.Fatal("expected ServerPreAuthConn")
|
||||
}
|
||||
if err := bc.SendAuthBanner("wrong-phase"); err == nil {
|
||||
t.Error("unexpected success of SendAuthBanner after authentication")
|
||||
} else if err != errSendBannerPhase {
|
||||
t.Errorf("unexpected error: %v; want %v", err, errSendBannerPhase)
|
||||
}
|
||||
}
|
||||
|
||||
type markerConn struct {
|
||||
closed uint32
|
||||
used uint32
|
||||
|
||||
+1
-1
@@ -459,7 +459,7 @@ func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel
|
||||
return nil, err
|
||||
}
|
||||
go DiscardRequests(in)
|
||||
return ch, err
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
type tcpChan struct {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user