Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18e0be4596 | ||
|
|
7a4a4d6bea | ||
|
|
1f7c531b64 | ||
|
|
234d0b5a5f | ||
|
|
a083cb74b6 | ||
|
|
b4dcfb54b8 | ||
|
|
b853e4b620 | ||
|
|
3f62bf119e | ||
|
|
86efde54dc | ||
|
|
a6cdac6084 | ||
|
|
39dc44e69c | ||
|
|
afebf4cb4e | ||
|
|
89f4e9bb5b | ||
|
|
71488c48c2 | ||
|
|
82adefa711 | ||
|
|
c757c9851f | ||
|
|
593c81af8a | ||
|
|
46efc8bc62 | ||
|
|
e0a54fdc4b |
+1
-1
@@ -221,7 +221,7 @@ func (c *Client) directoryURL() string {
|
||||
|
||||
// CreateCert was part of the old version of ACME. It is incompatible with RFC 8555.
|
||||
//
|
||||
// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateOrderCert.
|
||||
// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateCertFromOrder.
|
||||
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
|
||||
return nil, "", errPreRFC
|
||||
}
|
||||
|
||||
@@ -671,7 +671,7 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
||||
chain, _, err := client.CreateCertFromOrder(ctx, o, csr, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -182,9 +182,9 @@ func (p *prober) runOrder(ctx context.Context, identifiers []acme.AuthzID) {
|
||||
log.Fatalf("WaitOrder(%q): %v", o.URI, err)
|
||||
}
|
||||
csr, certkey := newCSR(identifiers)
|
||||
der, curl, err := p.client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
||||
der, curl, err := p.client.CreateCertFromOrder(ctx, o, csr, true)
|
||||
if err != nil {
|
||||
log.Fatalf("CreateOrderCert: %v", err)
|
||||
log.Fatalf("CreateCertFromOrder: %v", err)
|
||||
}
|
||||
log.Printf("cert URL: %s", curl)
|
||||
if err := checkCert(der, identifiers); err != nil {
|
||||
@@ -247,9 +247,9 @@ func (p *prober) runPreauthz(ctx context.Context, identifiers []acme.AuthzID) {
|
||||
log.Fatalf("WaitOrder(%q): %v", o.URI, err)
|
||||
}
|
||||
csr, certkey := newCSR(identifiers)
|
||||
der, curl, err := p.client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
||||
der, curl, err := p.client.CreateCertFromOrder(ctx, o, csr, true)
|
||||
if err != nil {
|
||||
log.Fatalf("CreateOrderCert: %v", err)
|
||||
log.Fatalf("CreateCertFromOrder: %v", err)
|
||||
}
|
||||
log.Printf("cert URL: %s", curl)
|
||||
if err := checkCert(der, identifiers); err != nil {
|
||||
|
||||
+5
-8
@@ -103,9 +103,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, ur
|
||||
}
|
||||
payload = base64.RawURLEncoding.EncodeToString(cs)
|
||||
}
|
||||
hash := sha.New()
|
||||
hash.Write([]byte(phead + "." + payload))
|
||||
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
||||
sig, err := jwsSign(key, sha, []byte(phead+"."+payload))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -195,14 +193,13 @@ func jwkEncode(pub crypto.PublicKey) (string, error) {
|
||||
return "", ErrUnsupportedKey
|
||||
}
|
||||
|
||||
// jwsSign signs the digest using the given key.
|
||||
// The hash is unused for ECDSA keys.
|
||||
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
||||
// jwsSign signs the payload using the given key.
|
||||
func jwsSign(key crypto.Signer, hash crypto.Hash, payload []byte) ([]byte, error) {
|
||||
switch pub := key.Public().(type) {
|
||||
case *rsa.PublicKey:
|
||||
return key.Sign(rand.Reader, digest, hash)
|
||||
return crypto.SignMessage(key, rand.Reader, payload, hash)
|
||||
case *ecdsa.PublicKey:
|
||||
sigASN1, err := key.Sign(rand.Reader, digest, hash)
|
||||
sigASN1, err := crypto.SignMessage(key, rand.Reader, payload, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+39
-5
@@ -14,6 +14,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
@@ -353,6 +354,22 @@ func (s *customTestSigner) Sign(io.Reader, []byte, crypto.SignerOpts) ([]byte, e
|
||||
return s.sig, nil
|
||||
}
|
||||
|
||||
type customTestMessageSigner struct {
|
||||
customTestSigner
|
||||
msg []byte
|
||||
opts crypto.SignerOpts
|
||||
}
|
||||
|
||||
func (s *customTestMessageSigner) Sign(io.Reader, []byte, crypto.SignerOpts) ([]byte, error) {
|
||||
return nil, errors.New("customTestSigner: Sign() called instead of SignMessage()")
|
||||
}
|
||||
|
||||
func (s *customTestMessageSigner) SignMessage(_ io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
s.msg = msg
|
||||
s.opts = opts
|
||||
return s.sig, nil
|
||||
}
|
||||
|
||||
func TestJWSEncodeJSONCustom(t *testing.T) {
|
||||
claims := struct{ Msg string }{"hello"}
|
||||
const (
|
||||
@@ -391,24 +408,33 @@ func TestJWSEncodeJSONCustom(t *testing.T) {
|
||||
)
|
||||
|
||||
tt := []struct {
|
||||
alg, phead string
|
||||
name, phead string
|
||||
pub crypto.PublicKey
|
||||
stdsig, jwsig string
|
||||
msgSigner bool
|
||||
}{
|
||||
{"ES256", es256phead, testKeyEC.Public(), es256stdsig, es256jwsig},
|
||||
{"RS256", rs256phead, testKey.Public(), testsig, testsig},
|
||||
{"ES256", es256phead, testKeyEC.Public(), es256stdsig, es256jwsig, false},
|
||||
{"RS256", rs256phead, testKey.Public(), testsig, testsig, false},
|
||||
{"ES256-MessageSigner", es256phead, testKeyEC.Public(), es256stdsig, es256jwsig, true},
|
||||
{"RS256-MessageSigner", rs256phead, testKey.Public(), testsig, testsig, true},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
tc := tc
|
||||
t.Run(tc.alg, func(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
stdsig, err := base64.RawStdEncoding.DecodeString(tc.stdsig)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't decode test vector: %v", err)
|
||||
}
|
||||
signer := &customTestSigner{
|
||||
cs := customTestSigner{
|
||||
sig: stdsig,
|
||||
pub: tc.pub,
|
||||
}
|
||||
var signer crypto.Signer = &cs
|
||||
var ms *customTestMessageSigner
|
||||
if tc.msgSigner {
|
||||
ms = &customTestMessageSigner{customTestSigner: cs}
|
||||
signer = ms
|
||||
}
|
||||
|
||||
b, err := jwsEncodeJSON(claims, signer, noKeyID, "nonce", "url")
|
||||
if err != nil {
|
||||
@@ -427,6 +453,14 @@ func TestJWSEncodeJSONCustom(t *testing.T) {
|
||||
if j.Sig != tc.jwsig {
|
||||
t.Errorf("j.Sig = %q\nwant %q", j.Sig, tc.jwsig)
|
||||
}
|
||||
if ms != nil {
|
||||
if want := tc.phead + "." + payload; string(ms.msg) != want {
|
||||
t.Errorf("msg = %q; want %q", ms.msg, want)
|
||||
}
|
||||
if ms.opts != crypto.SHA256 {
|
||||
t.Errorf("opts = %v; want %v", ms.opts, crypto.SHA256)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -37,7 +37,7 @@ import (
|
||||
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"
|
||||
pebbleModVersion = "v2.10.1"
|
||||
// startingPort is the first port number used for binding interface
|
||||
// addresses. Each call to takeNextPort() will increment a port number
|
||||
// starting at this value.
|
||||
@@ -420,7 +420,7 @@ func testIssuance(t *testing.T, env *environment, challSrv challengeServer) {
|
||||
}
|
||||
|
||||
// Finalize the order by creating a certificate with our CSR.
|
||||
chain, _, err := client.CreateOrderCert(ctx, order.FinalizeURL, csrDer, true)
|
||||
chain, _, err := client.CreateCertFromOrder(ctx, order, csrDer, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to finalize order %s with finalize URL %s: %v",
|
||||
orderURL, order.FinalizeURL, err)
|
||||
@@ -638,7 +638,7 @@ func startPebbleEnvironment(t *testing.T, config *environmentConfig) environment
|
||||
// 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),
|
||||
"-dnsserver", fmt.Sprintf(":%d", config.dnsPort),
|
||||
"-defaultIPv6", "",
|
||||
"-management", fmt.Sprintf(":%d", takeNextPort()),
|
||||
"-doh", "",
|
||||
|
||||
+47
-5
@@ -197,7 +197,7 @@ func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) e
|
||||
// identify those with StatusPending status and fulfill a challenge using Accept.
|
||||
// Once all authorizations are satisfied, the caller will typically want to poll
|
||||
// order status using WaitOrder until it's in StatusReady state.
|
||||
// To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert.
|
||||
// To finalize the order and obtain a certificate, the caller submits a CSR with CreateCertFromOrder.
|
||||
func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
|
||||
dir, err := c.Discover(ctx)
|
||||
if err != nil {
|
||||
@@ -250,7 +250,11 @@ func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
return responseOrder(res)
|
||||
o, err := responseOrder(res)
|
||||
if err == nil && o.URI == "" {
|
||||
o.URI = url
|
||||
}
|
||||
return o, err
|
||||
}
|
||||
|
||||
// WaitOrder polls an order from the given URL until it is in one of the final states,
|
||||
@@ -271,6 +275,9 @@ func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
|
||||
}
|
||||
o, err := responseOrder(res)
|
||||
res.Body.Close()
|
||||
if err == nil && o.URI == "" {
|
||||
o.URI = url
|
||||
}
|
||||
switch {
|
||||
case err != nil:
|
||||
// Skip and retry.
|
||||
@@ -338,11 +345,43 @@ func responseOrder(res *http.Response) (*Order, error) {
|
||||
// certificate chain. Otherwise, only a leaf certificate is returned.
|
||||
// The returned URL can be used to re-fetch the certificate using FetchCert.
|
||||
//
|
||||
// This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs.
|
||||
// This method is only supported by CAs implementing RFC 8555 that return a
|
||||
// Location header for order finalization responses. Most users should prefer
|
||||
// CreateCertFromOrder, an alternative that has better interoperation with RFC 8555
|
||||
// compliant CAs. See CreateCert for pre-RFC CAs.
|
||||
//
|
||||
// CreateOrderCert returns an error if the CA's response is unreasonably large.
|
||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||
func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||
return c.createOrderCert(ctx, url, "", csr, bundle)
|
||||
}
|
||||
|
||||
// CreateCertFromOrder submits the CSR (Certificate Signing Request) to a CA for
|
||||
// the specified Order created with AuthorizeOrder.
|
||||
//
|
||||
// If the bundle argument is true, the returned value also contain the CA (issuer)
|
||||
// certificate chain. Otherwise, only a leaf certificate is returned.
|
||||
// The returned URL can be used to re-fetch the certificate using FetchCert.
|
||||
//
|
||||
// This method is only supported by CAs implementing RFC 8555.
|
||||
// See CreateCert for pre-RFC CAs.
|
||||
//
|
||||
// CreateCertFromOrder returns an error if the CA's response is unreasonably large.
|
||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||
func (c *Client) CreateCertFromOrder(ctx context.Context, order *Order, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||
if order == nil {
|
||||
return nil, "", errors.New("acme: nil order")
|
||||
}
|
||||
if order.URI == "" {
|
||||
return nil, "", errors.New("acme: order URI is empty")
|
||||
}
|
||||
if order.FinalizeURL == "" {
|
||||
return nil, "", errors.New("acme: order finalize URL is empty")
|
||||
}
|
||||
return c.createOrderCert(ctx, order.FinalizeURL, order.URI, csr, bundle)
|
||||
}
|
||||
|
||||
func (c *Client) createOrderCert(ctx context.Context, finalizeURL, orderURL string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
|
||||
return nil, "", err
|
||||
}
|
||||
@@ -353,7 +392,7 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu
|
||||
}{
|
||||
CSR: base64.RawURLEncoding.EncodeToString(csr),
|
||||
}
|
||||
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||
res, err := c.post(ctx, nil, finalizeURL, req, wantStatus(http.StatusOK))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@@ -365,7 +404,10 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu
|
||||
|
||||
// Wait for CA to issue the cert if they haven't.
|
||||
if o.Status != StatusValid {
|
||||
o, err = c.WaitOrder(ctx, o.URI)
|
||||
if orderURL == "" {
|
||||
orderURL = o.URI
|
||||
}
|
||||
o, err = c.WaitOrder(ctx, orderURL)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
||||
@@ -979,6 +979,80 @@ func TestRFC_CreateOrderCert(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestRFC_CreateCertFromOrder tests that finalization responses work even when
|
||||
// they omit a Location header. Such a header isn't required by RFC 8555 in this
|
||||
// context and we should be able to complete an order without needing it.
|
||||
func TestRFC_CreateCertFromOrder(t *testing.T) {
|
||||
q := &x509.CertificateRequest{
|
||||
Subject: pkix.Name{CommonName: "example.org"},
|
||||
}
|
||||
csr, err := x509.CreateCertificateRequest(rand.Reader, q, testKeyEC)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tmpl := &x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||
leaf, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testKeyEC.PublicKey, testKeyEC)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s := newACMEServer()
|
||||
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Location", s.url("/accounts/1"))
|
||||
w.Write([]byte(`{"status": "valid"}`))
|
||||
})
|
||||
s.handle("/finalize", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, `{"status":"processing"}`)
|
||||
})
|
||||
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, `{"status":"valid", "certificate":%q}`, s.url("/crt"))
|
||||
})
|
||||
s.handle("/crt", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/pem-certificate-chain")
|
||||
pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: leaf})
|
||||
})
|
||||
s.start()
|
||||
defer s.close()
|
||||
|
||||
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
|
||||
order := &Order{URI: s.url("/orders/1"), FinalizeURL: s.url("/finalize")}
|
||||
cert, curl, err := cl.CreateCertFromOrder(context.Background(), order, csr, true)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateCertFromOrder: %v", err)
|
||||
}
|
||||
if _, err := x509.ParseCertificate(cert[0]); err != nil {
|
||||
t.Errorf("ParseCertificate: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(cert[0], leaf) {
|
||||
t.Errorf("cert and leaf bytes don't match")
|
||||
}
|
||||
if u := s.url("/crt"); curl != u {
|
||||
t.Errorf("curl = %q; want %q", curl, u)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRFC_CreateCertFromOrderInvalidOrder tests that invalid CreateCertFromOrder
|
||||
// calls fail before making a request.
|
||||
func TestRFC_CreateCertFromOrderInvalidOrder(t *testing.T) {
|
||||
cl := new(Client)
|
||||
tests := []struct {
|
||||
name string
|
||||
order *Order
|
||||
}{
|
||||
{"nil", nil},
|
||||
{"empty URI", &Order{FinalizeURL: "https://example.com/finalize"}},
|
||||
{"empty finalize URL", &Order{URI: "https://example.com/order/1"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if _, _, err := cl.CreateCertFromOrder(context.Background(), tt.order, nil, false); err == nil {
|
||||
t.Fatal("CreateCertFromOrder returned nil error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRFC_AlreadyRevokedCert(t *testing.T) {
|
||||
s := newACMEServer()
|
||||
s.handle("/acme/revoke-cert", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
+1
-1
@@ -330,7 +330,7 @@ type Order struct {
|
||||
// Possible values are StatusPending, StatusReady, StatusProcessing, StatusValid and StatusInvalid.
|
||||
// Pending means the CA does not believe that the client has fulfilled the requirements.
|
||||
// Ready indicates that the client has fulfilled all the requirements and can submit a CSR
|
||||
// to obtain a certificate. This is done with Client's CreateOrderCert.
|
||||
// to obtain a certificate. This is done with Client's CreateCertFromOrder.
|
||||
// Processing means the certificate is being issued.
|
||||
// Valid indicates the CA has issued the certificate. It can be downloaded
|
||||
// from the Order's CertURL. This is done with Client's FetchCert.
|
||||
|
||||
@@ -24,6 +24,7 @@ var (
|
||||
|
||||
func TestArgon2(t *testing.T) {
|
||||
defer func(sse4 bool) { useSSE4 = sse4 }(useSSE4)
|
||||
defer func(lsx bool) { useLSX = lsx }(useLSX)
|
||||
|
||||
if useSSE4 {
|
||||
t.Log("SSE4.1 version")
|
||||
@@ -32,6 +33,14 @@ func TestArgon2(t *testing.T) {
|
||||
testArgon2id(t)
|
||||
useSSE4 = false
|
||||
}
|
||||
|
||||
if useLSX {
|
||||
t.Log("LSX version")
|
||||
testArgon2i(t)
|
||||
testArgon2d(t)
|
||||
testArgon2id(t)
|
||||
useLSX = false
|
||||
}
|
||||
t.Log("generic version")
|
||||
testArgon2i(t)
|
||||
testArgon2d(t)
|
||||
|
||||
@@ -6,6 +6,10 @@ package argon2
|
||||
|
||||
var useSSE4 bool
|
||||
|
||||
// useLSX reports whether the loong64 LSX vector implementation should be
|
||||
// used. It is set from cpu.Loong64.HasLSX in blamka_loong64.go's init().
|
||||
var useLSX bool
|
||||
|
||||
func processBlockGeneric(out, in1, in2 *block, xor bool) {
|
||||
var t block
|
||||
for i := range t {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2026 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 loong64 && gc && !purego
|
||||
|
||||
package argon2
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
func init() {
|
||||
// useLSX (declared in blamka_generic.go) gates the LSX vector path.
|
||||
// If the running CPU lacks LSX support, processBlockVX falls back
|
||||
// to processBlockGeneric.
|
||||
useLSX = cpu.Loong64.HasLSX
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func blamkaLOONG64(b *block)
|
||||
|
||||
//go:noescape
|
||||
func mixBlocksVX(out, a, b, c *block)
|
||||
|
||||
//go:noescape
|
||||
func xorBlocksVX(out, a, b, c *block)
|
||||
|
||||
func processBlock(out, in1, in2 *block) {
|
||||
processBlockVX(out, in1, in2, false)
|
||||
}
|
||||
|
||||
func processBlockXOR(out, in1, in2 *block) {
|
||||
processBlockVX(out, in1, in2, true)
|
||||
}
|
||||
|
||||
func processBlockVX(out, in1, in2 *block, xor bool) {
|
||||
if !useLSX {
|
||||
processBlockGeneric(out, in1, in2, xor)
|
||||
return
|
||||
}
|
||||
|
||||
var t block
|
||||
mixBlocksVX(&t, in1, in2, &t)
|
||||
blamkaLOONG64(&t)
|
||||
if xor {
|
||||
xorBlocksVX(out, in1, in2, &t)
|
||||
} else {
|
||||
mixBlocksVX(out, in1, in2, &t)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
// Copyright 2026 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 loong64 && gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// HALF_ROUND applies one BlaMka half-round (mul-add-xor-rotate) to two
|
||||
// groups of 4 columns, each packed 2-wide into V registers (v0/v2/v4/v6
|
||||
// hold one pair of columns in lane0/lane1, v1/v3/v5/v7 hold the other).
|
||||
// t0, t1 are scratch registers, one per independent column group.
|
||||
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, t1) \
|
||||
VMULWEVVWU v0, v2, t0; \
|
||||
VADDV t0, t0, t0; \
|
||||
VADDV v0, v2, v0; \
|
||||
VADDV v0, t0, v0; \
|
||||
VXORV v6, v0, v6; \
|
||||
VROTRV $32, v6, v6; \
|
||||
VMULWEVVWU v1, v3, t1; \
|
||||
VADDV t1, t1, t1; \
|
||||
VADDV v1, v3, v1; \
|
||||
VADDV v1, t1, v1; \
|
||||
VXORV v7, v1, v7; \
|
||||
VROTRV $32, v7, v7; \
|
||||
VMULWEVVWU v4, v6, t0; \
|
||||
VADDV t0, t0, t0; \
|
||||
VADDV v4, v6, v4; \
|
||||
VADDV v4, t0, v4; \
|
||||
VXORV v2, v4, v2; \
|
||||
VROTRV $24, v2, v2; \
|
||||
VMULWEVVWU v5, v7, t1; \
|
||||
VADDV t1, t1, t1; \
|
||||
VADDV v5, v7, v5; \
|
||||
VADDV v5, t1, v5; \
|
||||
VXORV v3, v5, v3; \
|
||||
VROTRV $24, v3, v3; \
|
||||
VMULWEVVWU v0, v2, t0; \
|
||||
VADDV t0, t0, t0; \
|
||||
VADDV v0, v2, v0; \
|
||||
VADDV v0, t0, v0; \
|
||||
VXORV v6, v0, v6; \
|
||||
VROTRV $16, v6, v6; \
|
||||
VMULWEVVWU v1, v3, t1; \
|
||||
VADDV t1, t1, t1; \
|
||||
VADDV v1, v3, v1; \
|
||||
VADDV v1, t1, v1; \
|
||||
VXORV v7, v1, v7; \
|
||||
VROTRV $16, v7, v7; \
|
||||
VMULWEVVWU v4, v6, t0; \
|
||||
VADDV t0, t0, t0; \
|
||||
VADDV v4, v6, v4; \
|
||||
VADDV v4, t0, v4; \
|
||||
VXORV v2, v4, v2; \
|
||||
VROTRV $63, v2, v2; \
|
||||
VMULWEVVWU v5, v7, t1; \
|
||||
VADDV t1, t1, t1; \
|
||||
VADDV v5, v7, v5; \
|
||||
VADDV v5, t1, v5; \
|
||||
VXORV v3, v5, v3; \
|
||||
VROTRV $63, v3, v3
|
||||
|
||||
// SHUFFLE / SHUFFLE_INV: reused verbatim from blake2b_loong64.s -- amd64
|
||||
// avo source confirms argon2's SHUFFLE/SHUFFLE_INV and blake2b's SHUFFLE
|
||||
// are byte-for-byte identical macros (diagonalize / undiagonalize),
|
||||
// operating only on V2..V7 (V0/V1 untouched), with V13 held at zero.
|
||||
#define SHUFFLE \
|
||||
VADDV V2, V13, V12; \
|
||||
VADDV V6, V13, V16; \
|
||||
VADDV V4, V13, V14; \
|
||||
VADDV V5, V13, V4; \
|
||||
VADDV V14, V13, V5; \
|
||||
VSHUF4IV $9, V3, V2; \
|
||||
VSHUF4IV $9, V12, V3; \
|
||||
VSHUF4IV $3, V7, V6; \
|
||||
VSHUF4IV $3, V16, V7
|
||||
|
||||
#define SHUFFLE_INV \
|
||||
VADDV V2, V13, V12; \
|
||||
VADDV V6, V13, V16; \
|
||||
VADDV V4, V13, V14; \
|
||||
VADDV V5, V13, V4; \
|
||||
VADDV V14, V13, V5; \
|
||||
VSHUF4IV $3, V3, V2; \
|
||||
VSHUF4IV $3, V12, V3; \
|
||||
VSHUF4IV $9, V7, V6; \
|
||||
VSHUF4IV $9, V16, V7
|
||||
|
||||
// LOAD_MSG_0 / STORE_MSG_0: row pass. `off` is the byte offset of the
|
||||
// 128-byte (16-uint64) row group; within the macro the 8 loads step by
|
||||
// 16B (2 uint64 per V register). Called from blamkaLOONG64 with off
|
||||
// stepping by 128B across the 8 row groups -- do not confuse the two
|
||||
// strides.
|
||||
#define LOAD_MSG_0(off) \
|
||||
VMOVQ (off+0)(R4), V0; \
|
||||
VMOVQ (off+16)(R4), V1; \
|
||||
VMOVQ (off+32)(R4), V2; \
|
||||
VMOVQ (off+48)(R4), V3; \
|
||||
VMOVQ (off+64)(R4), V4; \
|
||||
VMOVQ (off+80)(R4), V5; \
|
||||
VMOVQ (off+96)(R4), V6; \
|
||||
VMOVQ (off+112)(R4), V7
|
||||
|
||||
#define STORE_MSG_0(off) \
|
||||
VMOVQ V0, (off+0)(R4); \
|
||||
VMOVQ V1, (off+16)(R4); \
|
||||
VMOVQ V2, (off+32)(R4); \
|
||||
VMOVQ V3, (off+48)(R4); \
|
||||
VMOVQ V4, (off+64)(R4); \
|
||||
VMOVQ V5, (off+80)(R4); \
|
||||
VMOVQ V6, (off+96)(R4); \
|
||||
VMOVQ V7, (off+112)(R4)
|
||||
|
||||
// LOAD_MSG_1 / STORE_MSG_1: diagonal pass. `off` is the byte offset within
|
||||
// a 128-byte row group; within the macro the 8 loads step by 128B (one
|
||||
// element from each of the 8 row groups, mirroring blamka_generic.go's
|
||||
// t[off], t[128+off], t[256+off], ...). Called from blamkaLOONG64 with
|
||||
// off stepping by 16B (2 uint64) across the 8 diagonal groups -- the
|
||||
// inner/outer strides are swapped relative to LOAD_MSG_0/STORE_MSG_0.
|
||||
#define LOAD_MSG_1(off) \
|
||||
VMOVQ (off+0)(R4), V0; \
|
||||
VMOVQ (off+128)(R4), V1; \
|
||||
VMOVQ (off+256)(R4), V2; \
|
||||
VMOVQ (off+384)(R4), V3; \
|
||||
VMOVQ (off+512)(R4), V4; \
|
||||
VMOVQ (off+640)(R4), V5; \
|
||||
VMOVQ (off+768)(R4), V6; \
|
||||
VMOVQ (off+896)(R4), V7
|
||||
|
||||
#define STORE_MSG_1(off) \
|
||||
VMOVQ V0, (off+0)(R4); \
|
||||
VMOVQ V1, (off+128)(R4); \
|
||||
VMOVQ V2, (off+256)(R4); \
|
||||
VMOVQ V3, (off+384)(R4); \
|
||||
VMOVQ V4, (off+512)(R4); \
|
||||
VMOVQ V5, (off+640)(R4); \
|
||||
VMOVQ V6, (off+768)(R4); \
|
||||
VMOVQ V7, (off+896)(R4)
|
||||
|
||||
// BLAMKA_ROUND_0(off): one full row pass over the 16-uint64 group at
|
||||
// byte offset off -- corresponds to a single call to blamkaGeneric
|
||||
// with the "row" argument order (t[off]..t[off+15]).
|
||||
#define BLAMKA_ROUND_0(off) \
|
||||
LOAD_MSG_0(off); \
|
||||
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||
SHUFFLE; \
|
||||
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||
SHUFFLE_INV; \
|
||||
STORE_MSG_0(off)
|
||||
|
||||
// BLAMKA_ROUND_1(off): one full diagonal pass over the 16 uint64s at
|
||||
// diagonal offset off -- corresponds to a single call to blamkaGeneric
|
||||
// with the "diagonal" argument order (t[off], t[off+1], t[16+off], ...).
|
||||
#define BLAMKA_ROUND_1(off) \
|
||||
LOAD_MSG_1(off); \
|
||||
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||
SHUFFLE; \
|
||||
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||
SHUFFLE_INV; \
|
||||
STORE_MSG_1(off)
|
||||
|
||||
// func blamkaLOONG64(b *block)
|
||||
TEXT ·blamkaLOONG64(SB), NOSPLIT, $0-8
|
||||
MOVV b+0(FP), R4
|
||||
|
||||
VXORV V13, V13, V13 // V13 = 0, used by SHUFFLE/SHUFFLE_INV
|
||||
|
||||
// Round 0: 8 row passes, stride 128 bytes (16 uint64s).
|
||||
BLAMKA_ROUND_0(0)
|
||||
BLAMKA_ROUND_0(128)
|
||||
BLAMKA_ROUND_0(256)
|
||||
BLAMKA_ROUND_0(384)
|
||||
BLAMKA_ROUND_0(512)
|
||||
BLAMKA_ROUND_0(640)
|
||||
BLAMKA_ROUND_0(768)
|
||||
BLAMKA_ROUND_0(896)
|
||||
|
||||
// Round 1: 8 diagonal passes, stride 16 bytes (2 uint64s).
|
||||
BLAMKA_ROUND_1(0)
|
||||
BLAMKA_ROUND_1(16)
|
||||
BLAMKA_ROUND_1(32)
|
||||
BLAMKA_ROUND_1(48)
|
||||
BLAMKA_ROUND_1(64)
|
||||
BLAMKA_ROUND_1(80)
|
||||
BLAMKA_ROUND_1(96)
|
||||
BLAMKA_ROUND_1(112)
|
||||
|
||||
RET
|
||||
|
||||
// func mixBlocksVX(out, a, b, c *block)
|
||||
TEXT ·mixBlocksVX(SB), NOSPLIT, $0-32
|
||||
MOVV out+0(FP), R4
|
||||
MOVV a+8(FP), R5
|
||||
MOVV b+16(FP), R6
|
||||
MOVV c+24(FP), R7
|
||||
MOVV $1024, R8
|
||||
|
||||
PCALIGN $16
|
||||
mixloop:
|
||||
VMOVQ (R5), V0
|
||||
VMOVQ (R6), V1
|
||||
VMOVQ (R7), V2
|
||||
VXORV V0, V1, V0
|
||||
VXORV V0, V2, V0
|
||||
VMOVQ V0, (R4)
|
||||
ADDV $16, R4
|
||||
ADDV $16, R5
|
||||
ADDV $16, R6
|
||||
ADDV $16, R7
|
||||
SUBV $16, R8
|
||||
BNE R8, R0, mixloop
|
||||
|
||||
RET
|
||||
|
||||
// func xorBlocksVX(out, a, b, c *block)
|
||||
TEXT ·xorBlocksVX(SB), NOSPLIT, $0-32
|
||||
MOVV out+0(FP), R4
|
||||
MOVV a+8(FP), R5
|
||||
MOVV b+16(FP), R6
|
||||
MOVV c+24(FP), R7
|
||||
MOVV $1024, R8
|
||||
|
||||
PCALIGN $16
|
||||
xorloop:
|
||||
VMOVQ (R5), V0
|
||||
VMOVQ (R6), V1
|
||||
VMOVQ (R7), V2
|
||||
VMOVQ (R4), V3
|
||||
VXORV V0, V1, V0
|
||||
VXORV V0, V2, V0
|
||||
VXORV V0, V3, V0
|
||||
VMOVQ V0, (R4)
|
||||
ADDV $16, R4
|
||||
ADDV $16, R5
|
||||
ADDV $16, R6
|
||||
ADDV $16, R7
|
||||
SUBV $16, R8
|
||||
BNE R8, R0, xorloop
|
||||
|
||||
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 !amd64 || purego || !gc
|
||||
//go:build (!amd64 && !loong64) || purego || !gc
|
||||
|
||||
package argon2
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2026 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 loong64 && gc && !purego
|
||||
|
||||
package blake2b
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
//go:noescape
|
||||
func hashBlocksVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
|
||||
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||
if cpu.Loong64.HasLSX {
|
||||
hashBlocksVX(h, c, flag, blocks)
|
||||
} else {
|
||||
hashBlocksGeneric(h, c, flag, blocks)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// Copyright 2026 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 loong64 && gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·iv0<>+0(SB)/8, $0x6a09e667f3bcc908
|
||||
DATA ·iv0<>+8(SB)/8, $0xbb67ae8584caa73b
|
||||
GLOBL ·iv0<>(SB), RODATA|NOPTR, $16
|
||||
|
||||
DATA ·iv1<>+0(SB)/8, $0x3c6ef372fe94f82b
|
||||
DATA ·iv1<>+8(SB)/8, $0xa54ff53a5f1d36f1
|
||||
GLOBL ·iv1<>(SB), RODATA|NOPTR, $16
|
||||
|
||||
DATA ·iv2<>+0(SB)/8, $0x510e527fade682d1
|
||||
DATA ·iv2<>+8(SB)/8, $0x9b05688c2b3e6c1f
|
||||
GLOBL ·iv2<>(SB), RODATA|NOPTR, $16
|
||||
|
||||
DATA ·iv3<>+0(SB)/8, $0x1f83d9abfb41bd6b
|
||||
DATA ·iv3<>+8(SB)/8, $0x5be0cd19137e2179
|
||||
GLOBL ·iv3<>(SB), RODATA|NOPTR, $16
|
||||
|
||||
#define SHUFFLE_1 \
|
||||
VADDV V2, V13, V12; \ // V12 = V2
|
||||
VADDV V6, V13, V16; \ // V16 = V6
|
||||
VADDV V4, V13, V14; \ // V14 = V4
|
||||
VADDV V5, V13, V4; \ // V4 = V5
|
||||
VADDV V14, V13, V5; \ // V5 = V4
|
||||
VSHUF4IV $9, V3, V2; \
|
||||
VSHUF4IV $9, V12, V3; \
|
||||
VSHUF4IV $3, V7, V6; \
|
||||
VSHUF4IV $3, V16, V7; \
|
||||
|
||||
#define SHUFFLE_2 \
|
||||
VADDV V2, V13, V12; \ // V12 = V2
|
||||
VADDV V6, V13, V16; \ // V16 = V6
|
||||
VADDV V4, V13, V14; \ // V14 = V4
|
||||
VADDV V5, V13, V4; \ // V4 = V5
|
||||
VADDV V14, V13, V5; \ // V5 = V4
|
||||
VSHUF4IV $3, V3, V2; \
|
||||
VSHUF4IV $3, V12, V3; \
|
||||
VSHUF4IV $9, V7, V6; \
|
||||
VSHUF4IV $9, V16, V7; \
|
||||
|
||||
#define LOAD_M(a, b, c, d, e, f, g, h) \
|
||||
VMOVQ a, V8.V[0]; \
|
||||
VMOVQ b, V8.V[1]; \
|
||||
VMOVQ c, V9.V[0]; \
|
||||
VMOVQ d, V9.V[1]; \
|
||||
VMOVQ e, V10.V[0]; \
|
||||
VMOVQ f, V10.V[1]; \
|
||||
VMOVQ g, V11.V[0]; \
|
||||
VMOVQ h, V11.V[1]; \
|
||||
|
||||
#define ROUND_0 \
|
||||
VADDV V0, V8, V0; \
|
||||
VADDV V1, V9, V1; \
|
||||
VADDV V0, V2, V0; \
|
||||
VADDV V1, V3, V1; \
|
||||
VXORV V6, V0, V6; \
|
||||
VXORV V7, V1, V7; \
|
||||
VROTRV $32, V6, V6; \
|
||||
VROTRV $32, V7, V7; \
|
||||
VADDV V4, V6, V4; \
|
||||
VADDV V5, V7, V5; \
|
||||
VXORV V2, V4, V2; \
|
||||
VXORV V3, V5, V3; \
|
||||
VROTRV $24, V2, V2; \
|
||||
VROTRV $24, V3, V3; \
|
||||
VADDV V0, V10, V0; \
|
||||
VADDV V1, V11, V1; \
|
||||
VADDV V0, V2, V0; \
|
||||
VADDV V1, V3, V1; \
|
||||
VXORV V6, V0, V6; \
|
||||
VXORV V7, V1, V7; \
|
||||
VROTRV $16, V6, V6; \
|
||||
VROTRV $16, V7, V7; \
|
||||
VADDV V4, V6, V4; \
|
||||
VADDV V5, V7, V5; \
|
||||
VXORV V2, V4, V2; \
|
||||
VXORV V3, V5, V3; \
|
||||
VROTRV $63, V2, V2; \
|
||||
VROTRV $63, V3, V3; \
|
||||
|
||||
#define ROUND_8 ROUND_0
|
||||
|
||||
// func hashBlocksVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
TEXT ·hashBlocksVX(SB), NOSPLIT, $0-48
|
||||
MOVV h+0(FP), R4
|
||||
MOVV c+8(FP), R5
|
||||
MOVV flag+16(FP), R6
|
||||
MOVV blocks_base+24(FP), R7
|
||||
MOVV blocks_len+32(FP), R8
|
||||
MOVV (R5), R9 // c0
|
||||
MOVV 8(R5), R10 // c1
|
||||
|
||||
loop:
|
||||
ADDV $0x80, R9
|
||||
SGTU $0x80, R9, R11
|
||||
ADDV R10, R11, R10
|
||||
VMOVQ R9, V8.V[0]
|
||||
VMOVQ R10, V8.V[1]
|
||||
|
||||
MOVV $·iv0<>(SB), R11
|
||||
MOVV $·iv1<>(SB), R12
|
||||
MOVV $·iv2<>(SB), R13
|
||||
MOVV $·iv3<>(SB), R14
|
||||
VMOVQ (R13), V6
|
||||
VMOVQ (R11), V4
|
||||
VMOVQ (R12), V5
|
||||
MOVV (R14), R15 // v14
|
||||
MOVV 8(R14), R16
|
||||
VXORV V6, V8, V6
|
||||
XOR R15, R6, R15
|
||||
VMOVQ R16, V7.V[1]
|
||||
VMOVQ R15, V7.V[0]
|
||||
|
||||
VMOVQ (R4), V0
|
||||
VMOVQ 16(R4), V1
|
||||
VMOVQ 32(R4), V2
|
||||
VMOVQ 48(R4), V3
|
||||
|
||||
MOVV 0(R7), R11
|
||||
MOVV 8(R7), R12
|
||||
MOVV 16(R7), R13
|
||||
MOVV 24(R7), R14
|
||||
MOVV 32(R7), R15
|
||||
MOVV 40(R7), R16
|
||||
MOVV 48(R7), R17
|
||||
MOVV 56(R7), R18
|
||||
MOVV 64(R7), R19
|
||||
MOVV 72(R7), R24
|
||||
MOVV 80(R7), R25
|
||||
MOVV 88(R7), R26
|
||||
MOVV 96(R7), R27
|
||||
MOVV 104(R7), R28
|
||||
MOVV 112(R7), R29
|
||||
MOVV 120(R7), R30
|
||||
|
||||
VXORV V13, V13, V13 // V13=0
|
||||
|
||||
LOAD_M(R11, R13, R15, R17, R12, R14, R16, R18)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R19, R25, R27, R29, R24, R26, R28, R30)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R29, R15, R24, R28, R25, R19, R30, R17)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R12, R11, R26, R16, R27, R13, R18, R14)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R26, R27, R16, R30, R19, R11, R13, R28)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R25, R14, R18, R24, R29, R17, R12, R15)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R18, R14, R28, R26, R24, R12, R27, R29)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R13, R16, R15, R30, R17, R25, R11, R19)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R24, R16, R13, R25, R11, R18, R15, R30)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R29, R26, R17, R14, R12, R27, R19, R28)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R13, R17, R11, R19, R27, R25, R26, R14)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R15, R18, R30, R12, R28, R16, R29, R24)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R27, R12, R29, R15, R16, R30, R28, R25)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R11, R17, R24, R19, R18, R14, R13, R26)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R28, R18, R27, R14, R26, R29, R12, R24)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R16, R30, R19, R13, R11, R15, R17, R25)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R17, R29, R26, R11, R30, R24, R14, R19)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R27, R28, R12, R25, R13, R18, R15, R16)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R25, R19, R18, R12, R13, R15, R17, R16)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R30, R24, R14, R28, R26, R29, R27, R11)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R11, R13, R15, R17, R12, R14, R16, R18)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R19, R25, R27, R29, R24, R26, R28, R30)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
LOAD_M(R29, R15, R24, R28, R25, R19, R30, R17)
|
||||
ROUND_0
|
||||
SHUFFLE_1
|
||||
LOAD_M(R12, R11, R26, R16, R27, R13, R18, R14)
|
||||
ROUND_8
|
||||
SHUFFLE_2
|
||||
|
||||
VMOVQ (R4), V8
|
||||
VMOVQ 16(R4), V9
|
||||
VMOVQ 32(R4), V10
|
||||
VMOVQ 48(R4), V11
|
||||
VXORV V0, V4, V0
|
||||
VXORV V1, V5, V1
|
||||
VXORV V2, V6, V2
|
||||
VXORV V3, V7, V3
|
||||
VXORV V0, V8, V0
|
||||
VXORV V1, V9, V1
|
||||
VXORV V2, V10, V2
|
||||
VXORV V3, V11, V3
|
||||
VMOVQ V0, (R4)
|
||||
VMOVQ V1, 16(R4)
|
||||
VMOVQ V2, 32(R4)
|
||||
VMOVQ V3, 48(R4)
|
||||
|
||||
SUBV $128, R8
|
||||
ADDV $128, R7
|
||||
BNE R8, R0, loop
|
||||
|
||||
MOVV R9, (R5)
|
||||
MOVV R10, 8(R5)
|
||||
|
||||
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 !amd64 || purego || !gc
|
||||
//go:build (!amd64 && !loong64) || purego || !gc
|
||||
|
||||
package blake2b
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2026 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 chacha20
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
//go:noescape
|
||||
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||
if cpu.Loong64.HasLSX {
|
||||
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||
} else {
|
||||
c.xorKeyStreamBlocksGeneric(dst, src)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
// derived from chacha_arm64.s
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·constants+0x00(SB)/4, $0x61707865
|
||||
DATA ·constants+0x04(SB)/4, $0x3320646e
|
||||
DATA ·constants+0x08(SB)/4, $0x79622d32
|
||||
DATA ·constants+0x0c(SB)/4, $0x6b206574
|
||||
GLOBL ·constants(SB), NOPTR|RODATA, $32
|
||||
|
||||
DATA ·incRotMatrix+0x00(SB)/4, $0x00000000
|
||||
DATA ·incRotMatrix+0x04(SB)/4, $0x00000001
|
||||
DATA ·incRotMatrix+0x08(SB)/4, $0x00000002
|
||||
DATA ·incRotMatrix+0x0c(SB)/4, $0x00000003
|
||||
GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32
|
||||
|
||||
#define NUM_ROUNDS 10
|
||||
|
||||
// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||
TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0
|
||||
MOVV dst+0(FP), R4
|
||||
MOVV src+24(FP), R5
|
||||
MOVV src_len+32(FP), R6
|
||||
MOVV key+48(FP), R7
|
||||
MOVV nonce+56(FP), R8
|
||||
MOVV counter+64(FP), R9
|
||||
|
||||
MOVV $·constants(SB), R10
|
||||
MOVV $·incRotMatrix(SB), R11
|
||||
|
||||
MOVW (R9), R12
|
||||
|
||||
loop:
|
||||
MOVV $NUM_ROUNDS, R15
|
||||
// load 4-32bit data from incRotMatrix added to counter
|
||||
VMOVQ (R11), V30
|
||||
|
||||
// load contants
|
||||
VMOVQ (R10), V0.W4
|
||||
VMOVQ 4(R10), V1.W4
|
||||
VMOVQ 8(R10), V2.W4
|
||||
VMOVQ 12(R10), V3.W4
|
||||
|
||||
// load keys
|
||||
VMOVQ (R7), V4.W4
|
||||
VMOVQ 4(R7), V5.W4
|
||||
VMOVQ 8(R7), V6.W4
|
||||
VMOVQ 12(R7), V7.W4
|
||||
VMOVQ 16(R7), V8.W4
|
||||
VMOVQ 20(R7), V9.W4
|
||||
VMOVQ 24(R7), V10.W4
|
||||
VMOVQ 28(R7), V11.W4
|
||||
|
||||
// load counter + nonce
|
||||
VMOVQ (R9), V12.W4
|
||||
|
||||
VMOVQ (R8), V13.W4
|
||||
VMOVQ 4(R8), V14.W4
|
||||
VMOVQ 8(R8), V15.W4
|
||||
|
||||
// update counter
|
||||
VADDW V30, V12, V12
|
||||
|
||||
chacha:
|
||||
// V0..V3 += V4..V7
|
||||
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 16)
|
||||
VADDW V0, V4, V0
|
||||
VADDW V1, V5, V1
|
||||
VADDW V2, V6, V2
|
||||
VADDW V3, V7, V3
|
||||
VXORV V12, V0, V12
|
||||
VXORV V13, V1, V13
|
||||
VXORV V14, V2, V14
|
||||
VXORV V15, V3, V15
|
||||
VROTRW $16, V12, V12
|
||||
VROTRW $16, V13, V13
|
||||
VROTRW $16, V14, V14
|
||||
VROTRW $16, V15, V15
|
||||
|
||||
// V8..V11 += V12..V15
|
||||
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 12)
|
||||
VADDW V8, V12, V8
|
||||
VADDW V9, V13, V9
|
||||
VADDW V10, V14, V10
|
||||
VADDW V11, V15, V11
|
||||
VXORV V4, V8, V4
|
||||
VXORV V5, V9, V5
|
||||
VXORV V6, V10, V6
|
||||
VXORV V7, V11, V7
|
||||
VROTRW $20, V4, V4
|
||||
VROTRW $20, V5, V5
|
||||
VROTRW $20, V6, V6
|
||||
VROTRW $20, V7, V7
|
||||
|
||||
// V0..V3 += V4..V7
|
||||
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 8)
|
||||
VADDW V0, V4, V0
|
||||
VADDW V1, V5, V1
|
||||
VADDW V2, V6, V2
|
||||
VADDW V3, V7, V3
|
||||
VXORV V12, V0, V12
|
||||
VXORV V13, V1, V13
|
||||
VXORV V14, V2, V14
|
||||
VXORV V15, V3, V15
|
||||
VROTRW $24, V12, V12
|
||||
VROTRW $24, V13, V13
|
||||
VROTRW $24, V14, V14
|
||||
VROTRW $24, V15, V15
|
||||
|
||||
// V8..V11 += V12..V15
|
||||
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 7)
|
||||
VADDW V12, V8, V8
|
||||
VADDW V13, V9, V9
|
||||
VADDW V14, V10, V10
|
||||
VADDW V15, V11, V11
|
||||
VXORV V4, V8, V4
|
||||
VXORV V5, V9, V5
|
||||
VXORV V6, V10, V6
|
||||
VXORV V7, V11, V7
|
||||
VROTRW $25, V4, V4
|
||||
VROTRW $25, V5, V5
|
||||
VROTRW $25, V6, V6
|
||||
VROTRW $25, V7, V7
|
||||
|
||||
// V0..V3 += V5..V7, V4
|
||||
// V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16)
|
||||
VADDW V0, V5, V0
|
||||
VADDW V1, V6, V1
|
||||
VADDW V2, V7, V2
|
||||
VADDW V3, V4, V3
|
||||
VXORV V15, V0, V15
|
||||
VXORV V12, V1, V12
|
||||
VXORV V13, V2, V13
|
||||
VXORV V14, V3, V14
|
||||
VROTRW $16, V15, V15
|
||||
VROTRW $16, V12, V12
|
||||
VROTRW $16, V13, V13
|
||||
VROTRW $16, V14, V14
|
||||
|
||||
// V10,V11,V8,V9 += V15,V12,V13,V14
|
||||
// V5,V6,V7,V4 <<<= ((V5,V6,V7,V4 XOR V10,V11,V8,V9), 12)
|
||||
VADDW V10, V15, V10
|
||||
VADDW V11, V12, V11
|
||||
VADDW V8, V13, V8
|
||||
VADDW V9, V14, V9
|
||||
VXORV V5, V10, V5
|
||||
VXORV V6, V11, V6
|
||||
VXORV V7, V8, V7
|
||||
VXORV V4, V9, V4
|
||||
VROTRW $20, V5, V5
|
||||
VROTRW $20, V6, V6
|
||||
VROTRW $20, V7, V7
|
||||
VROTRW $20, V4, V4
|
||||
|
||||
// V0..V3 += V5..V7, V4
|
||||
// V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 8)
|
||||
VADDW V5, V0, V0
|
||||
VADDW V6, V1, V1
|
||||
VADDW V7, V2, V2
|
||||
VADDW V4, V3, V3
|
||||
VXORV V15, V0, V15
|
||||
VXORV V12, V1, V12
|
||||
VXORV V13, V2, V13
|
||||
VXORV V14, V3, V14
|
||||
VROTRW $24, V15, V15
|
||||
VROTRW $24, V12, V12
|
||||
VROTRW $24, V13, V13
|
||||
VROTRW $24, V14, V14
|
||||
|
||||
// V10,V11,V8,V9 += V15,V12,V13,V14
|
||||
// V5,V6,V7,V4 <<<= ((V5,V6,V7,V4 XOR V10,V11,V8,V9), 7)
|
||||
VADDW V15, V10, V10
|
||||
VADDW V12, V11, V11
|
||||
VADDW V13, V8, V8
|
||||
VADDW V14, V9, V9
|
||||
VXORV V5, V10, V5
|
||||
VXORV V6, V11, V6
|
||||
VXORV V7, V8, V7
|
||||
VXORV V4, V9, V4
|
||||
VROTRW $25, V5, V5
|
||||
VROTRW $25, V6, V6
|
||||
VROTRW $25, V7, V7
|
||||
VROTRW $25, V4, V4
|
||||
|
||||
SUBV $1, R15
|
||||
BNE R15, R0, chacha
|
||||
|
||||
// load origin contants
|
||||
VMOVQ (R10), V16.W4
|
||||
VMOVQ 4(R10), V17.W4
|
||||
VMOVQ 8(R10), V18.W4
|
||||
VMOVQ 12(R10), V19.W4
|
||||
|
||||
// load origin keys
|
||||
VMOVQ (R7), V20.W4
|
||||
VMOVQ 4(R7), V21.W4
|
||||
VMOVQ 8(R7), V22.W4
|
||||
VMOVQ 12(R7), V23.W4
|
||||
VMOVQ 16(R7), V24.W4
|
||||
VMOVQ 20(R7), V25.W4
|
||||
VMOVQ 24(R7), V26.W4
|
||||
VMOVQ 28(R7), V27.W4
|
||||
|
||||
// add back the initial state to generate the key stream
|
||||
VADDW V30, V12, V12 // update counter in advance to prevent V30 from being overwritten
|
||||
VADDW V16, V0, V0
|
||||
VADDW V17, V1, V1
|
||||
VADDW V18, V2, V2
|
||||
VADDW V19, V3, V3
|
||||
|
||||
// load origin counter + nonce
|
||||
VMOVQ (R9), V28.W4
|
||||
VMOVQ (R8), V29.W4
|
||||
VMOVQ 4(R8), V30.W4
|
||||
VMOVQ 8(R8), V31.W4
|
||||
|
||||
VADDW V20, V4, V4
|
||||
VADDW V21, V5, V5
|
||||
VADDW V22, V6, V6
|
||||
VADDW V23, V7, V7
|
||||
VADDW V24, V8, V8
|
||||
VADDW V25, V9, V9
|
||||
VADDW V26, V10, V10
|
||||
VADDW V27, V11, V11
|
||||
VADDW V28, V12, V12
|
||||
VADDW V29, V13, V13
|
||||
VADDW V30, V14, V14
|
||||
VADDW V31, V15, V15
|
||||
|
||||
// shuffle
|
||||
VILVLW V0, V1, V16
|
||||
VILVHW V0, V1, V17
|
||||
VILVLW V2, V3, V18
|
||||
VILVHW V2, V3, V19
|
||||
VILVLW V4, V5 ,V20
|
||||
VILVHW V4, V5, V21
|
||||
VILVLW V6, V7, V22
|
||||
VILVHW V6, V7, V23
|
||||
VILVLW V8, V9, V24
|
||||
VILVHW V8, V9, V25
|
||||
VILVLW V10, V11, V26
|
||||
VILVHW V10, V11, V27
|
||||
VILVLW V12, V13, V28
|
||||
VILVHW V12, V13, V29
|
||||
VILVLW V14, V15, V30
|
||||
VILVHW V14, V15, V31
|
||||
VILVLV V16, V18, V0
|
||||
VILVHV V16, V18, V4
|
||||
VILVLV V17, V19, V8
|
||||
VILVHV V17, V19, V12
|
||||
|
||||
// load src data from R5
|
||||
VMOVQ 0(R5), V16
|
||||
VMOVQ 16(R5), V17
|
||||
VMOVQ 32(R5), V18
|
||||
VMOVQ 48(R5), V19
|
||||
|
||||
VILVLV V20, V22, V1
|
||||
VILVHV V20, V22, V5
|
||||
VILVLV V21, V23, V9
|
||||
VILVHV V21, V23, V13
|
||||
|
||||
VMOVQ 64(R5), V20
|
||||
VMOVQ 80(R5), V21
|
||||
VMOVQ 96(R5), V22
|
||||
VMOVQ 112(R5), V23
|
||||
|
||||
VILVLV V24, V26, V2
|
||||
VILVHV V24, V26, V6
|
||||
VILVLV V25, V27, V10
|
||||
VILVHV V25, V27, V14
|
||||
|
||||
VMOVQ 128(R5), V24
|
||||
VMOVQ 144(R5), V25
|
||||
VMOVQ 160(R5), V26
|
||||
VMOVQ 176(R5), V27
|
||||
|
||||
VILVLV V28, V30, V3
|
||||
VILVHV V28, V30, V7
|
||||
VILVLV V29, V31, V11
|
||||
VILVHV V29, V31, V15
|
||||
|
||||
VMOVQ 192(R5), V28
|
||||
VMOVQ 208(R5), V29
|
||||
VMOVQ 224(R5), V30
|
||||
VMOVQ 240(R5), V31
|
||||
|
||||
VXORV V0, V16, V16
|
||||
VXORV V1, V17, V17
|
||||
VXORV V2, V18, V18
|
||||
VXORV V3, V19, V19
|
||||
|
||||
VMOVQ V16, 0(R4)
|
||||
VMOVQ V17, 16(R4)
|
||||
VMOVQ V18, 32(R4)
|
||||
VMOVQ V19, 48(R4)
|
||||
|
||||
VXORV V4, V20, V20
|
||||
VXORV V5, V21, V21
|
||||
VXORV V6, V22, V22
|
||||
VXORV V7, V23, V23
|
||||
|
||||
VMOVQ V20, 64(R4)
|
||||
VMOVQ V21, 80(R4)
|
||||
VMOVQ V22, 96(R4)
|
||||
VMOVQ V23, 112(R4)
|
||||
|
||||
VXORV V8, V24, V24
|
||||
VXORV V9, V25, V25
|
||||
VXORV V10, V26, V26
|
||||
VXORV V11, V27, V27
|
||||
|
||||
VMOVQ V24, 128(R4)
|
||||
VMOVQ V25, 144(R4)
|
||||
VMOVQ V26, 160(R4)
|
||||
VMOVQ V27, 176(R4)
|
||||
|
||||
VXORV V12, V28, V28
|
||||
VXORV V13, V29, V29
|
||||
VXORV V14, V30, V30
|
||||
VXORV V15, V31, V31
|
||||
|
||||
VMOVQ V28, 192(R4)
|
||||
VMOVQ V29, 208(R4)
|
||||
VMOVQ V30, 224(R4)
|
||||
VMOVQ V31, 240(R4)
|
||||
|
||||
ADD $4, R12, R12
|
||||
MOVW R12, (R9) // update counter
|
||||
|
||||
ADDV $256, R4, R4
|
||||
ADDV $256, R5, R5
|
||||
SUBV $256, R6, R6
|
||||
BNE R6, R0, loop
|
||||
|
||||
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 (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
|
||||
//go:build (!arm64 && !loong64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module golang.org/x/crypto
|
||||
|
||||
go 1.25.0
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.57.0 // tagx:ignore
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/term v0.45.0
|
||||
golang.org/x/net v0.58.0 // tagx:ignore
|
||||
golang.org/x/sys v0.48.0
|
||||
golang.org/x/term v0.46.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.41.0 // indirect
|
||||
require golang.org/x/text v0.42.0 // indirect
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2026 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 loong64 && !purego && gc
|
||||
|
||||
package salsa
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
// xorKeyStreamVX is implemented in salsa20_loong64.s.
|
||||
//
|
||||
//go:noescape
|
||||
func xorKeyStreamVX(out, in *byte, n uint64, nonce, key *byte)
|
||||
|
||||
// XORKeyStream crypts bytes from in to out using the given key and counters.
|
||||
// In and out must overlap entirely or not at all. Counter
|
||||
// contains the raw salsa20 counter bytes (both nonce and block counter).
|
||||
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||
if len(in) == 0 {
|
||||
return
|
||||
}
|
||||
_ = out[len(in)-1]
|
||||
if cpu.Loong64.HasLSX {
|
||||
xorKeyStreamVX(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0])
|
||||
} else {
|
||||
genericXORKeyStream(out, in, counter, key)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
// Copyright 2026 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 !purego && gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·constants+0x00(SB)/4, $0x61707865
|
||||
DATA ·constants+0x04(SB)/4, $0x3320646e
|
||||
DATA ·constants+0x08(SB)/4, $0x79622d32
|
||||
DATA ·constants+0x0c(SB)/4, $0x6b206574
|
||||
GLOBL ·constants(SB), NOPTR|RODATA, $32
|
||||
|
||||
#define NUM_ROUNDS 10
|
||||
|
||||
// func xorKeyStreamVX(out, in *byte, n uint64, counter, key *byte)
|
||||
TEXT ·xorKeyStreamVX(SB), $0-40
|
||||
MOVV out+0(FP), R4
|
||||
MOVV in+8(FP), R5
|
||||
MOVV n+16(FP), R6
|
||||
MOVV counter+24(FP), R7
|
||||
MOVV key+32(FP), R8
|
||||
MOVV $·constants(SB), R10
|
||||
|
||||
BGE R0, R6, ret
|
||||
|
||||
MOVV 8(R7), R9 // counter[8:16]
|
||||
|
||||
loop256:
|
||||
MOVV $NUM_ROUNDS, R15
|
||||
VXORV V30, V30, V30 // V30 = 0
|
||||
|
||||
// load contants
|
||||
VMOVQ (R10), V0.W4
|
||||
VMOVQ 4(R10), V5.W4
|
||||
VMOVQ 8(R10), V10.W4
|
||||
VMOVQ 12(R10), V15.W4
|
||||
|
||||
// load keys
|
||||
VMOVQ (R8), V1.W4
|
||||
VMOVQ 4(R8), V2.W4
|
||||
VMOVQ 8(R8), V3.W4
|
||||
VMOVQ 12(R8), V4.W4
|
||||
VMOVQ 16(R8), V11.W4
|
||||
VMOVQ 20(R8), V12.W4
|
||||
VMOVQ 24(R8), V13.W4
|
||||
VMOVQ 28(R8), V14.W4
|
||||
|
||||
// load and update counter
|
||||
VMOVQ (R7), V6.W4
|
||||
VMOVQ 4(R7), V7.W4
|
||||
|
||||
ADDV $1, R9, R11
|
||||
ADDV $2, R9, R12
|
||||
ADDV $3, R9, R13
|
||||
VMOVQ R9, V8.W[0]
|
||||
VMOVQ R11, V8.W[1]
|
||||
VMOVQ R12, V8.W[2]
|
||||
VMOVQ R13, V8.W[3]
|
||||
SRLV $32, R9, R14
|
||||
SRLV $32, R11, R11
|
||||
SRLV $32, R12, R12
|
||||
SRLV $32, R13, R13
|
||||
VMOVQ R14, V9.W[0]
|
||||
VMOVQ R11, V9.W[1]
|
||||
VMOVQ R12, V9.W[2]
|
||||
VMOVQ R13, V9.W[3]
|
||||
|
||||
// backup V8 and V9
|
||||
VADDV V8, V30, V24 // V21 = V8
|
||||
VADDV V9, V30, V25 // V22 = V9
|
||||
|
||||
salsa20_256:
|
||||
VADDW V0, V12, V26
|
||||
VADDW V5, V1, V27
|
||||
VADDW V10, V6, V28
|
||||
VADDW V15, V11, V29
|
||||
VROTRW $25, V26, V26
|
||||
VROTRW $25, V27, V27
|
||||
VROTRW $25, V28, V28
|
||||
VROTRW $25, V29, V29
|
||||
VXORV V4, V26, V4
|
||||
VXORV V9, V27, V9
|
||||
VXORV V14, V28, V14
|
||||
VXORV V3, V29, V3
|
||||
VADDW V4, V0, V26
|
||||
VADDW V9, V5, V27
|
||||
VADDW V14, V10, V28
|
||||
VADDW V3, V15, V29
|
||||
VROTRW $23, V26, V26
|
||||
VROTRW $23, V27, V27
|
||||
VROTRW $23, V28, V28
|
||||
VROTRW $23, V29, V29
|
||||
VXORV V8, V26, V8
|
||||
VXORV V13, V27, V13
|
||||
VXORV V2, V28, V2
|
||||
VXORV V7, V29, V7
|
||||
VADDW V8, V4, V26
|
||||
VADDW V13, V9, V27
|
||||
VADDW V2, V14, V28
|
||||
VADDW V7, V3, V29
|
||||
VROTRW $19, V26, V26
|
||||
VROTRW $19, V27, V27
|
||||
VROTRW $19, V28, V28
|
||||
VROTRW $19, V29, V29
|
||||
VXORV V12, V26, V12
|
||||
VXORV V1, V27, V1
|
||||
VXORV V6, V28, V6
|
||||
VXORV V11, V29, V11
|
||||
VADDW V12, V8, V26
|
||||
VADDW V1, V13, V27
|
||||
VADDW V6, V2, V28
|
||||
VADDW V11, V7, V29
|
||||
VROTRW $14, V26, V26
|
||||
VROTRW $14, V27, V27
|
||||
VROTRW $14, V28, V28
|
||||
VROTRW $14, V29, V29
|
||||
VXORV V0, V26, V0
|
||||
VXORV V5, V27, V5
|
||||
VXORV V10, V28, V10
|
||||
VXORV V15, V29, V15
|
||||
|
||||
VADDW V0, V3, V26
|
||||
VADDW V5, V4, V27
|
||||
VADDW V10, V9, V28
|
||||
VADDW V15, V14, V29
|
||||
VROTRW $25, V26, V26
|
||||
VROTRW $25, V27, V27
|
||||
VROTRW $25, V28, V28
|
||||
VROTRW $25, V29, V29
|
||||
VXORV V1, V26, V1
|
||||
VXORV V6, V27, V6
|
||||
VXORV V11, V28, V11
|
||||
VXORV V12, V29, V12
|
||||
VADDW V1, V0, V26
|
||||
VADDW V6, V5, V27
|
||||
VADDW V11, V10, V28
|
||||
VADDW V12, V15, V29
|
||||
VROTRW $23, V26, V26
|
||||
VROTRW $23, V27, V27
|
||||
VROTRW $23, V28, V28
|
||||
VROTRW $23, V29, V29
|
||||
VXORV V2, V26, V2
|
||||
VXORV V7, V27, V7
|
||||
VXORV V8, V28, V8
|
||||
VXORV V13, V29, V13
|
||||
VADDW V2, V1, V26
|
||||
VADDW V7, V6, V27
|
||||
VADDW V8, V11, V28
|
||||
VADDW V13, V12, V29
|
||||
VROTRW $19, V26, V26
|
||||
VROTRW $19, V27, V27
|
||||
VROTRW $19, V28, V28
|
||||
VROTRW $19, V29, V29
|
||||
VXORV V3, V26, V3
|
||||
VXORV V4, V27, V4
|
||||
VXORV V9, V28, V9
|
||||
VXORV V14, V29, V14
|
||||
VADDW V3, V2, V26
|
||||
VADDW V4, V7, V27
|
||||
VADDW V9, V8, V28
|
||||
VADDW V14, V13, V29
|
||||
VROTRW $14, V26, V26
|
||||
VROTRW $14, V27, V27
|
||||
VROTRW $14, V28, V28
|
||||
VROTRW $14, V29, V29
|
||||
VXORV V0, V26, V0
|
||||
VXORV V5, V27, V5
|
||||
VXORV V10, V28, V10
|
||||
VXORV V15, V29, V15
|
||||
|
||||
SUBV $1, R15
|
||||
BNE R15, R0, salsa20_256
|
||||
|
||||
// load origin contants
|
||||
VMOVQ (R10), V16.W4
|
||||
VMOVQ 4(R10), V21.W4
|
||||
VMOVQ 8(R10), V26.W4
|
||||
VMOVQ 12(R10), V31.W4
|
||||
|
||||
// load origin keys
|
||||
VMOVQ (R8), V17.W4
|
||||
VMOVQ 4(R8), V18.W4
|
||||
VMOVQ 8(R8), V19.W4
|
||||
VMOVQ 12(R8), V20.W4
|
||||
VMOVQ 16(R8), V27.W4
|
||||
VMOVQ 20(R8), V28.W4
|
||||
VMOVQ 24(R8), V29.W4
|
||||
VMOVQ 28(R8), V30.W4
|
||||
|
||||
// load origin counter
|
||||
VMOVQ (R7), V22.W4
|
||||
VMOVQ 4(R7), V23.W4
|
||||
|
||||
// add back the initial state to generate the key stream
|
||||
VADDW V0, V16, V0
|
||||
VADDW V1, V17, V1
|
||||
VADDW V2, V18, V2
|
||||
VADDW V3, V19, V3
|
||||
VADDW V4, V20, V4
|
||||
VADDW V5, V21, V5
|
||||
VADDW V6, V22, V6
|
||||
VADDW V7, V23, V7
|
||||
VADDW V8, V24, V8
|
||||
VADDW V9, V25, V9
|
||||
VADDW V10, V26, V10
|
||||
VADDW V11, V27, V11
|
||||
VADDW V12, V28, V12
|
||||
VADDW V13, V29, V13
|
||||
VADDW V14, V30, V14
|
||||
VADDW V15, V31, V15
|
||||
|
||||
// shuffle
|
||||
VILVLW V0, V1, V16
|
||||
VILVHW V0, V1, V17
|
||||
VILVLW V2, V3, V18
|
||||
VILVHW V2, V3, V19
|
||||
VILVLW V4, V5 ,V20
|
||||
VILVHW V4, V5, V21
|
||||
VILVLW V6, V7, V22
|
||||
VILVHW V6, V7, V23
|
||||
VILVLW V8, V9, V24
|
||||
VILVHW V8, V9, V25
|
||||
VILVLW V10, V11, V26
|
||||
VILVHW V10, V11, V27
|
||||
VILVLW V12, V13, V28
|
||||
VILVHW V12, V13, V29
|
||||
VILVLW V14, V15, V30
|
||||
VILVHW V14, V15, V31
|
||||
VILVLV V16, V18, V0
|
||||
VILVHV V16, V18, V4
|
||||
VILVLV V17, V19, V8
|
||||
VILVHV V17, V19, V12
|
||||
VILVLV V20, V22, V1
|
||||
VILVHV V20, V22, V5
|
||||
VILVLV V21, V23, V9
|
||||
VILVHV V21, V23, V13
|
||||
VILVLV V24, V26, V2
|
||||
VILVHV V24, V26, V6
|
||||
VILVLV V25, V27, V10
|
||||
VILVHV V25, V27, V14
|
||||
VILVLV V28, V30, V3
|
||||
VILVHV V28, V30, V7
|
||||
VILVLV V29, V31, V11
|
||||
VILVHV V29, V31, V15
|
||||
|
||||
SGTU $256, R6, R11
|
||||
BNE R11, R0, less_than_256
|
||||
|
||||
// load src data from R5
|
||||
VMOVQ 0(R5), V16
|
||||
VMOVQ 16(R5), V17
|
||||
VMOVQ 32(R5), V18
|
||||
VMOVQ 48(R5), V19
|
||||
VMOVQ 64(R5), V20
|
||||
VMOVQ 80(R5), V21
|
||||
VMOVQ 96(R5), V22
|
||||
VMOVQ 112(R5), V23
|
||||
VMOVQ 128(R5), V24
|
||||
VMOVQ 144(R5), V25
|
||||
VMOVQ 160(R5), V26
|
||||
VMOVQ 176(R5), V27
|
||||
VMOVQ 192(R5), V28
|
||||
VMOVQ 208(R5), V29
|
||||
VMOVQ 224(R5), V30
|
||||
VMOVQ 240(R5), V31
|
||||
|
||||
VXORV V0, V16, V16
|
||||
VXORV V1, V17, V17
|
||||
VXORV V2, V18, V18
|
||||
VXORV V3, V19, V19
|
||||
VXORV V4, V20, V20
|
||||
VXORV V5, V21, V21
|
||||
VXORV V6, V22, V22
|
||||
VXORV V7, V23, V23
|
||||
VXORV V8, V24, V24
|
||||
VXORV V9, V25, V25
|
||||
VXORV V10, V26, V26
|
||||
VXORV V11, V27, V27
|
||||
VXORV V12, V28, V28
|
||||
VXORV V13, V29, V29
|
||||
VXORV V14, V30, V30
|
||||
VXORV V15, V31, V31
|
||||
|
||||
VMOVQ V16, 0(R4)
|
||||
VMOVQ V17, 16(R4)
|
||||
VMOVQ V18, 32(R4)
|
||||
VMOVQ V19, 48(R4)
|
||||
VMOVQ V20, 64(R4)
|
||||
VMOVQ V21, 80(R4)
|
||||
VMOVQ V22, 96(R4)
|
||||
VMOVQ V23, 112(R4)
|
||||
VMOVQ V24, 128(R4)
|
||||
VMOVQ V25, 144(R4)
|
||||
VMOVQ V26, 160(R4)
|
||||
VMOVQ V27, 176(R4)
|
||||
VMOVQ V28, 192(R4)
|
||||
VMOVQ V29, 208(R4)
|
||||
VMOVQ V30, 224(R4)
|
||||
VMOVQ V31, 240(R4)
|
||||
|
||||
ADDV $4, R9, R9 // update counter
|
||||
|
||||
SUBV $256, R6, R6
|
||||
ADDV $256, R4, R4
|
||||
ADDV $256, R5, R5
|
||||
SGTU $256, R6, R11
|
||||
BEQ R11, R0, loop256
|
||||
BEQ R6, R0, ret
|
||||
|
||||
less_than_256:
|
||||
VXORV V30, V30, V30 // V30=0
|
||||
SGTU $128, R6, R11
|
||||
BNE R11, R0, less_than_128
|
||||
SUBV $128, R6
|
||||
VMOVQ (R5), V16
|
||||
VMOVQ 16(R5), V17
|
||||
VMOVQ 32(R5), V18
|
||||
VMOVQ 48(R5), V19
|
||||
VMOVQ 64(R5), V20
|
||||
VMOVQ 80(R5), V21
|
||||
VMOVQ 96(R5), V22
|
||||
VMOVQ 112(R5), V23
|
||||
VXORV V0, V16, V16
|
||||
VXORV V1, V17, V17
|
||||
VXORV V2, V18, V18
|
||||
VXORV V3, V19, V19
|
||||
VXORV V4, V20, V20
|
||||
VXORV V5, V21, V21
|
||||
VXORV V6, V22, V22
|
||||
VXORV V7, V23, V23
|
||||
VMOVQ V16, (R4)
|
||||
VMOVQ V17, 16(R4)
|
||||
VMOVQ V18, 32(R4)
|
||||
VMOVQ V19, 48(R4)
|
||||
VMOVQ V20, 64(R4)
|
||||
VMOVQ V21, 80(R4)
|
||||
VMOVQ V22, 96(R4)
|
||||
VMOVQ V23, 112(R4)
|
||||
BEQ R6, R0, ret
|
||||
ADDV $128, R5, R5
|
||||
ADDV $128, R4, R4
|
||||
VADDV V8, V30, V0
|
||||
VADDV V9, V30, V1
|
||||
VADDV V10, V30, V2
|
||||
VADDV V11, V30, V3
|
||||
VADDV V12, V30, V4
|
||||
VADDV V13, V30, V5
|
||||
VADDV V14, V30, V6
|
||||
VADDV V15, V30, V7
|
||||
|
||||
less_than_128:
|
||||
SGTU $64, R6, R11
|
||||
BNE R11, R0, less_than_64
|
||||
SUBV $64, R6
|
||||
VMOVQ (R5), V16
|
||||
VMOVQ 16(R5), V17
|
||||
VMOVQ 32(R5), V18
|
||||
VMOVQ 48(R5), V19
|
||||
VXORV V0, V16, V16
|
||||
VXORV V1, V17, V17
|
||||
VXORV V2, V18, V18
|
||||
VXORV V3, V19, V19
|
||||
VMOVQ V16, (R4)
|
||||
VMOVQ V17, 16(R4)
|
||||
VMOVQ V18, 32(R4)
|
||||
VMOVQ V19, 48(R4)
|
||||
BEQ R6, R0, ret
|
||||
ADDV $64, R5
|
||||
ADDV $64, R4
|
||||
VADDV V4, V30, V0
|
||||
VADDV V5, V30, V1
|
||||
VADDV V6, V30, V2
|
||||
VADDV V7, V30, V3
|
||||
|
||||
less_than_64:
|
||||
SGTU $32, R6, R11
|
||||
BNE R11, R0, less_than_32
|
||||
SUBV $32, R6
|
||||
VMOVQ (R5), V16
|
||||
VMOVQ 16(R5), V17
|
||||
VXORV V0, V16, V16
|
||||
VXORV V1, V17, V17
|
||||
VMOVQ V16, (R4)
|
||||
VMOVQ V17, 16(R4)
|
||||
BEQ R6, R0, ret
|
||||
ADDV $32, R5
|
||||
ADDV $32, R4
|
||||
VADDV V2, V30, V0
|
||||
VADDV V3, V30, V1
|
||||
|
||||
less_than_32:
|
||||
SGTU $16, R6, R11
|
||||
BNE R11, R0, less_than_16
|
||||
SUBV $16, R6
|
||||
VMOVQ (R5), V16
|
||||
VXORV V16, V0, V16
|
||||
VMOVQ V16, (R4)
|
||||
BEQ R6, R0, ret
|
||||
ADDV $16, R5
|
||||
ADDV $16, R4
|
||||
VADDV V1, V30, V0
|
||||
|
||||
less_than_16:
|
||||
SGTU $8, R6, R11
|
||||
BNE R11, R0, less_than_8
|
||||
SUBV $8, R6
|
||||
VMOVQ V0.V[0], R11
|
||||
VMOVQ V0.V[1], R13
|
||||
MOVV (R5), R12
|
||||
XOR R11, R12, R12
|
||||
MOVV R12, (R4)
|
||||
BEQ R6, R0, ret
|
||||
ADDV $8, R5
|
||||
ADDV $8, R4
|
||||
VMOVQ R13, V0.V[0]
|
||||
|
||||
less_than_8:
|
||||
SGTU $4, R6, R11
|
||||
BNE R11, R0, less_than_4
|
||||
SUBV $4, R6
|
||||
VMOVQ V0.W[0], R11
|
||||
VMOVQ V0.W[1], R13
|
||||
MOVWU (R5), R12
|
||||
XOR R11, R12, R12
|
||||
MOVWU R12, (R4)
|
||||
BEQ R6, R0, ret
|
||||
ADDV $4, R5
|
||||
ADDV $4, R4
|
||||
VMOVQ R13, V0.W[0]
|
||||
|
||||
less_than_4:
|
||||
SGTU $2, R6, R11
|
||||
BNE R11, R0, less_than_2
|
||||
SUBV $2, R6
|
||||
VMOVQ V0.H[0], R11
|
||||
VMOVQ V0.H[1], R13
|
||||
MOVHU (R5), R12
|
||||
XOR R11, R12, R12
|
||||
MOVHU R12, (R4)
|
||||
BEQ R6, R0, ret
|
||||
ADDV $2, R5
|
||||
ADDV $2, R4
|
||||
VMOVQ R13, V0.H[0]
|
||||
|
||||
less_than_2:
|
||||
VMOVQ V0.B[0], R11
|
||||
MOVBU (R5), R12
|
||||
XOR R11, R12, R12
|
||||
MOVBU R12, (R4)
|
||||
|
||||
ret:
|
||||
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 !amd64 || purego || !gc
|
||||
//go:build (!amd64 && !loong64) || purego || !gc
|
||||
|
||||
package salsa
|
||||
|
||||
|
||||
@@ -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 && !purego && gc
|
||||
//go:build (amd64 || loong64) && !purego && gc
|
||||
|
||||
package salsa
|
||||
|
||||
+17
-21
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"slices"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
@@ -305,8 +306,11 @@ const sourceAddressCriticalOption = "source-address"
|
||||
// minimally, the IsAuthority callback should be set.
|
||||
type CertChecker struct {
|
||||
// SupportedCriticalOptions lists the CriticalOptions that the
|
||||
// server application layer understands. These are only used
|
||||
// for user certificates.
|
||||
// application layer understands. A certificate carrying a critical
|
||||
// option that is not listed here is rejected.
|
||||
// CertChecker.Authenticate additionally accepts the source-address
|
||||
// option, which the server enforces on the Permissions that
|
||||
// Authenticate returns.
|
||||
SupportedCriticalOptions []string
|
||||
|
||||
// IsUserAuthority should return true if the key is recognized as an
|
||||
@@ -369,8 +373,9 @@ func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey)
|
||||
return c.CheckCert(hostname, cert)
|
||||
}
|
||||
|
||||
// Authenticate checks a user certificate. Authenticate can be used as
|
||||
// a value for ServerConfig.PublicKeyCallback.
|
||||
// Authenticate checks a user certificate. Authenticate can be used as a value
|
||||
// for ServerConfig.PublicKeyCallback. The source-address critical option is
|
||||
// allowed, as it will be enforced by the server.
|
||||
func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) {
|
||||
cert, ok := pubKey.(*Certificate)
|
||||
if !ok {
|
||||
@@ -389,8 +394,11 @@ func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permis
|
||||
if !c.IsUserAuthority(cert.SignatureKey) {
|
||||
return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority")
|
||||
}
|
||||
|
||||
if err := c.CheckCert(conn.User(), cert); err != nil {
|
||||
// The source-address critical option is enforced by serverAuthenticate,
|
||||
// so it is supported regardless of SupportedCriticalOptions
|
||||
cc := *c
|
||||
cc.SupportedCriticalOptions = append(slices.Clip(cc.SupportedCriticalOptions), sourceAddressCriticalOption)
|
||||
if err := cc.CheckCert(conn.User(), cert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -398,27 +406,15 @@ func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permis
|
||||
}
|
||||
|
||||
// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and
|
||||
// the signature of the certificate.
|
||||
// the signature of the certificate. Critical options that are not listed in
|
||||
// SupportedCriticalOptions are rejected.
|
||||
func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
|
||||
if c.IsRevoked != nil && c.IsRevoked(cert) {
|
||||
return fmt.Errorf("ssh: certificate serial %d revoked", cert.Serial)
|
||||
}
|
||||
|
||||
for opt := range cert.CriticalOptions {
|
||||
// sourceAddressCriticalOption will be enforced by
|
||||
// serverAuthenticate
|
||||
if opt == sourceAddressCriticalOption {
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, supp := range c.SupportedCriticalOptions {
|
||||
if supp == opt {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
if !slices.Contains(c.SupportedCriticalOptions, opt) {
|
||||
return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,6 +385,82 @@ func TestHostKeyCert(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type testConnMeta struct {
|
||||
ConnMetadata
|
||||
user string
|
||||
}
|
||||
|
||||
func (c testConnMeta) User() string { return c.user }
|
||||
|
||||
func TestCertCriticalOptions(t *testing.T) {
|
||||
appended := make([]string, 2)
|
||||
appended[0], appended[1] = "supported-option", "appended-option"
|
||||
supported := appended[:1]
|
||||
|
||||
checker := &CertChecker{
|
||||
SupportedCriticalOptions: supported,
|
||||
IsHostAuthority: func(p PublicKey, addr string) bool {
|
||||
return bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal())
|
||||
},
|
||||
IsUserAuthority: func(p PublicKey) bool {
|
||||
return bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal())
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
opts map[string]string
|
||||
// succeed is the expected outcome of CheckHostKey, for a host
|
||||
// certificate, and of CheckCert, for a user certificate.
|
||||
succeed bool
|
||||
// authSucceed is the expected outcome of Authenticate.
|
||||
authSucceed bool
|
||||
}{
|
||||
{name: "no critical options", opts: nil, succeed: true, authSucceed: true},
|
||||
{name: "source-address", opts: map[string]string{sourceAddressCriticalOption: "192.168.1.0/24"}, authSucceed: true},
|
||||
{name: "unknown option", opts: map[string]string{"unknown-option": ""}},
|
||||
{name: "supported option", opts: map[string]string{"supported-option": ""}, succeed: true, authSucceed: true},
|
||||
} {
|
||||
hostCert := &Certificate{
|
||||
ValidPrincipals: []string{"hostname"},
|
||||
Key: testPublicKeys["rsa"],
|
||||
ValidBefore: CertTimeInfinity,
|
||||
CertType: HostCert,
|
||||
Permissions: Permissions{CriticalOptions: test.opts},
|
||||
}
|
||||
if err := hostCert.SignCert(rand.Reader, testSigners["ecdsa"]); err != nil {
|
||||
t.Fatalf("SignCert: %v", err)
|
||||
}
|
||||
|
||||
if err := checker.CheckHostKey("hostname:22", nil, hostCert); (err == nil) != test.succeed {
|
||||
t.Errorf("CheckHostKey(%s): got %v, want success=%v", test.name, err, test.succeed)
|
||||
}
|
||||
|
||||
userCert := &Certificate{
|
||||
ValidPrincipals: []string{"user"},
|
||||
Key: testPublicKeys["rsa"],
|
||||
ValidBefore: CertTimeInfinity,
|
||||
CertType: UserCert,
|
||||
Permissions: Permissions{CriticalOptions: test.opts},
|
||||
}
|
||||
if err := userCert.SignCert(rand.Reader, testSigners["ecdsa"]); err != nil {
|
||||
t.Fatalf("SignCert: %v", err)
|
||||
}
|
||||
|
||||
if err := checker.CheckCert("user", userCert); (err == nil) != test.succeed {
|
||||
t.Errorf("CheckCert(%s): got %v, want success=%v", test.name, err, test.succeed)
|
||||
}
|
||||
|
||||
if _, err := checker.Authenticate(testConnMeta{user: "user"}, userCert); (err == nil) != test.authSucceed {
|
||||
t.Errorf("Authenticate(%s): got %v, want success=%v", test.name, err, test.authSucceed)
|
||||
}
|
||||
}
|
||||
|
||||
if appended[1] != "appended-option" {
|
||||
t.Errorf("Authenticate overwrote the caller's slice: got %q, want %q", appended[1], "appended-option")
|
||||
}
|
||||
}
|
||||
|
||||
type legacyRSASigner struct {
|
||||
Signer
|
||||
}
|
||||
|
||||
+19
-1
@@ -173,6 +173,12 @@ type channel struct {
|
||||
// (for outbound channels) or received (for inbound channels).
|
||||
decided bool
|
||||
|
||||
// established is set to true once the channel is open and may carry normal
|
||||
// channel traffic: for an outbound channel when the peer's open
|
||||
// confirmation is received, for an inbound channel when the local side
|
||||
// accepts it. It is set and read from different goroutines.
|
||||
established atomic.Bool
|
||||
|
||||
// direction contains either channelOutbound, for channels created
|
||||
// locally, or channelInbound, for channels created by the peer.
|
||||
direction channelDirection
|
||||
@@ -434,10 +440,20 @@ func (ch *channel) responseMessageReceived() error {
|
||||
return errors.New("ssh: duplicate response received for channel")
|
||||
}
|
||||
ch.decided = true
|
||||
ch.established.Store(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch *channel) handlePacket(packet []byte) error {
|
||||
// Only the open response is expected before the channel is established.
|
||||
if !ch.established.Load() {
|
||||
switch packet[0] {
|
||||
case msgChannelOpenConfirm, msgChannelOpenFailure:
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch packet[0] {
|
||||
case msgChannelData, msgChannelExtendedData:
|
||||
return ch.handleData(packet)
|
||||
@@ -503,7 +519,8 @@ func (ch *channel) handlePacket(packet []byte) error {
|
||||
default:
|
||||
}
|
||||
default:
|
||||
ch.msg <- msg
|
||||
// No other message type is expected on an established channel.
|
||||
return fmt.Errorf("ssh: unexpected message type %d on channel %d", packet[0], ch.localId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -554,6 +571,7 @@ func (ch *channel) Accept() (Channel, <-chan *Request, error) {
|
||||
MaxPacketSize: ch.maxIncomingPayload,
|
||||
}
|
||||
ch.decided = true
|
||||
ch.established.Store(true)
|
||||
if err := ch.sendMessage(confirm); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -129,6 +129,10 @@ func init() {
|
||||
// You should expect that an active attacker can recover plaintext if
|
||||
// you do.
|
||||
cipherModes[InsecureCipherAES128CBC] = &cipherMode{16, aes.BlockSize, newAESCBCCipher}
|
||||
// AES-192 and AES-256 reuse the same CBC packet cipher; aes.NewCipher
|
||||
// selects the key schedule from the key length.
|
||||
cipherModes[InsecureCipherAES192CBC] = &cipherMode{24, aes.BlockSize, newAESCBCCipher}
|
||||
cipherModes[InsecureCipherAES256CBC] = &cipherMode{32, aes.BlockSize, newAESCBCCipher}
|
||||
cipherModes[InsecureCipherTripleDESCBC] = &cipherMode{24, des.BlockSize, newTripleDESCBCCipher}
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -37,6 +37,8 @@ const (
|
||||
CipherAES192CTR = "aes192-ctr"
|
||||
CipherAES256CTR = "aes256-ctr"
|
||||
InsecureCipherAES128CBC = "aes128-cbc"
|
||||
InsecureCipherAES192CBC = "aes192-cbc"
|
||||
InsecureCipherAES256CBC = "aes256-cbc"
|
||||
InsecureCipherTripleDESCBC = "3des-cbc"
|
||||
InsecureCipherRC4 = "arcfour"
|
||||
InsecureCipherRC4128 = "arcfour128"
|
||||
@@ -128,6 +130,8 @@ var (
|
||||
// package and which have security issues.
|
||||
insecureCiphers = []string{
|
||||
InsecureCipherAES128CBC,
|
||||
InsecureCipherAES192CBC,
|
||||
InsecureCipherAES256CBC,
|
||||
InsecureCipherTripleDESCBC,
|
||||
InsecureCipherRC4256,
|
||||
InsecureCipherRC4128,
|
||||
@@ -437,7 +441,7 @@ func (a *DirectionAlgorithms) rekeyBytes() int64 {
|
||||
// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
|
||||
// 128.
|
||||
switch a.Cipher {
|
||||
case CipherAES128CTR, CipherAES192CTR, CipherAES256CTR, CipherAES128GCM, CipherAES256GCM, InsecureCipherAES128CBC:
|
||||
case CipherAES128CTR, CipherAES192CTR, CipherAES256CTR, CipherAES128GCM, CipherAES256GCM, InsecureCipherAES128CBC, InsecureCipherAES192CBC, InsecureCipherAES256CBC:
|
||||
return 16 * (1 << 32)
|
||||
|
||||
}
|
||||
|
||||
@@ -142,6 +142,15 @@ func keyEq(a, b ssh.PublicKey) bool {
|
||||
return bytes.Equal(a.Marshal(), b.Marshal())
|
||||
}
|
||||
|
||||
// plainKeyBlob returns the serialized public portion of key: for a
|
||||
// certificate this is the certified key, otherwise the key itself.
|
||||
func plainKeyBlob(key ssh.PublicKey) string {
|
||||
if cert, ok := key.(*ssh.Certificate); ok {
|
||||
return string(cert.Key.Marshal())
|
||||
}
|
||||
return string(key.Marshal())
|
||||
}
|
||||
|
||||
// IsHostAuthority can be used as a callback in ssh.CertChecker
|
||||
func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool {
|
||||
h, p, err := net.SplitHostPort(address)
|
||||
@@ -160,10 +169,10 @@ func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool
|
||||
|
||||
// IsRevoked can be used as a callback in ssh.CertChecker
|
||||
func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool {
|
||||
if _, ok := db.revoked[string(key.Marshal())]; ok {
|
||||
if _, ok := db.revoked[plainKeyBlob(key)]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := db.revoked[string(key.SignatureKey.Marshal())]; ok {
|
||||
if _, ok := db.revoked[plainKeyBlob(key.SignatureKey)]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -228,7 +237,7 @@ func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error
|
||||
}
|
||||
|
||||
if marker == markerRevoked {
|
||||
db.revoked[string(key.Marshal())] = &KnownKey{
|
||||
db.revoked[plainKeyBlob(key)] = &KnownKey{
|
||||
Key: key,
|
||||
Filename: filename,
|
||||
Line: linenum,
|
||||
@@ -341,7 +350,7 @@ func (r *RevokedError) Error() string {
|
||||
// check checks a key against the host database. This should not be
|
||||
// used for verifying certificates.
|
||||
func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.PublicKey) error {
|
||||
if revoked := db.revoked[string(remoteKey.Marshal())]; revoked != nil {
|
||||
if revoked := db.revoked[plainKeyBlob(remoteKey)]; revoked != nil {
|
||||
return &RevokedError{Revoked: *revoked}
|
||||
}
|
||||
|
||||
|
||||
@@ -316,6 +316,68 @@ func TestRevokedCA(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevokedCertifiedKey(t *testing.T) {
|
||||
_, caPriv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
caSigner, err := ssh.NewSignerFromKey(caPriv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, hostPriv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hostKey, err := ssh.NewPublicKey(hostPriv.Public())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
signCert := func(serial uint64, principal string) *ssh.Certificate {
|
||||
cert := &ssh.Certificate{
|
||||
CertType: ssh.HostCert,
|
||||
Key: hostKey,
|
||||
Serial: serial,
|
||||
ValidBefore: ssh.CertTimeInfinity,
|
||||
ValidPrincipals: []string{principal},
|
||||
}
|
||||
if err := cert.SignCert(rand.Reader, caSigner); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return cert
|
||||
}
|
||||
cert := signCert(1, "server.org")
|
||||
|
||||
// Revoking the host key revokes any certificate for it, even when
|
||||
// the CA is trusted, matching OpenSSH's check_key_not_revoked.
|
||||
db := testDB(t, "@revoked server.org "+serialize(hostKey)+"\n"+
|
||||
"@cert-authority server.org "+serialize(caSigner.PublicKey())+"\n")
|
||||
if !db.IsRevoked(cert) {
|
||||
t.Error("IsRevoked returned false for certificate with revoked certified key")
|
||||
}
|
||||
|
||||
// Conversely, a @revoked line containing a certificate revokes the
|
||||
// plain key it certifies.
|
||||
db = testDB(t, "@revoked server.org "+serialize(cert)+"\n")
|
||||
if err := db.check("server.org:22", testAddr, hostKey); err == nil {
|
||||
t.Error("no error for plain key revoked via certificate")
|
||||
} else if _, ok := err.(*RevokedError); !ok {
|
||||
t.Errorf("got %T (%v), want *RevokedError", err, err)
|
||||
}
|
||||
|
||||
// Since only public portions are compared, revoking a certificate
|
||||
// also revokes any sibling certificate certifying the same key,
|
||||
// whatever its serial number and principals.
|
||||
sibling := signCert(2, "other.org")
|
||||
db = testDB(t, "@revoked * "+serialize(cert)+"\n"+
|
||||
"@cert-authority * "+serialize(caSigner.PublicKey())+"\n")
|
||||
if !db.IsRevoked(sibling) {
|
||||
t.Error("IsRevoked returned false for sibling certificate of a revoked certificate")
|
||||
}
|
||||
}
|
||||
|
||||
const testHostname = "hostname"
|
||||
|
||||
// generated with keygen -H -f
|
||||
|
||||
+198
-1
@@ -5,9 +5,11 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -1181,6 +1183,13 @@ func TestChannelUnexpectedResponsesDiscarded(t *testing.T) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Follow the flood with a request so the client can synchronize with
|
||||
// its mux loop. Once the client receives this request, the mux loop has
|
||||
// necessarily processed (and discarded) all the preceding responses.
|
||||
if _, err := serverCh.SendRequest("sync", false, nil); err != nil {
|
||||
done <- fmt.Errorf("send sync request: %w", err)
|
||||
return
|
||||
}
|
||||
// Echo any legitimate request back.
|
||||
for req := range serverCh.incomingRequests {
|
||||
if req.WantReply {
|
||||
@@ -1193,9 +1202,17 @@ func TestChannelUnexpectedResponsesDiscarded(t *testing.T) {
|
||||
done <- nil
|
||||
}()
|
||||
|
||||
req, ok := <-clientCh.incomingRequests
|
||||
if !ok {
|
||||
t.Fatal("channel closed before sync request")
|
||||
}
|
||||
if req.Type != "sync" {
|
||||
t.Fatalf("unexpected sync request type %q", req.Type)
|
||||
}
|
||||
|
||||
// If the flood had wedged the mux loop, this SendRequest would never
|
||||
// receive a reply.
|
||||
ok, err := clientCh.SendRequest("ping", true, []byte("hello"))
|
||||
ok, err = clientCh.SendRequest("ping", true, []byte("hello"))
|
||||
if err != nil {
|
||||
t.Fatalf("SendRequest: %v", err)
|
||||
}
|
||||
@@ -1213,6 +1230,186 @@ func TestChannelUnexpectedResponsesDiscarded(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelUndecidedOutboundDropsTraffic(t *testing.T) {
|
||||
// Traffic on an outbound channel the peer has not confirmed yet is dropped;
|
||||
// the open confirmation is still processed.
|
||||
a, b := memPipe()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
// Drain the peer end so that a write would not block.
|
||||
go func() {
|
||||
for {
|
||||
if _, err := b.readPacket(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
m := newMux(a)
|
||||
ch := m.newChannel("session", channelOutbound, nil)
|
||||
if ch.decided {
|
||||
t.Fatal("freshly opened outbound channel should not be decided")
|
||||
}
|
||||
|
||||
// Flooding channel requests must not block.
|
||||
reqPacket := Marshal(channelRequestMsg{PeersID: ch.localId, Request: "x"})
|
||||
for range chanSize * 4 {
|
||||
if err := ch.handlePacket(reqPacket); err != nil {
|
||||
t.Fatalf("handlePacket(channelRequest): %v", err)
|
||||
}
|
||||
}
|
||||
if n := len(ch.incomingRequests); n != 0 {
|
||||
t.Fatalf("incomingRequests should be empty, has %d entries", n)
|
||||
}
|
||||
|
||||
// A close before the confirmation must not remove or close the channel, and
|
||||
// must not emit a close for the still-unknown remote id.
|
||||
if err := ch.handlePacket(Marshal(channelCloseMsg{PeersID: ch.localId})); err != nil {
|
||||
t.Fatalf("handlePacket(channelClose): %v", err)
|
||||
}
|
||||
if m.chanList.getChan(ch.localId) != ch {
|
||||
t.Fatal("premature close removed the channel from the chanList")
|
||||
}
|
||||
|
||||
// The open confirmation is still processed and decides the channel.
|
||||
confirm := Marshal(channelOpenConfirmMsg{
|
||||
PeersID: ch.localId,
|
||||
MyID: 42,
|
||||
MyWindow: 1 << 20,
|
||||
MaxPacketSize: 1 << 15,
|
||||
})
|
||||
if err := ch.handlePacket(confirm); err != nil {
|
||||
t.Fatalf("handlePacket(openConfirm): %v", err)
|
||||
}
|
||||
if !ch.decided {
|
||||
t.Fatal("channel not decided after open confirmation")
|
||||
}
|
||||
if _, ok := (<-ch.msg).(*channelOpenConfirmMsg); !ok {
|
||||
t.Fatal("open confirmation was not delivered on ch.msg")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelUndecidedInboundDropsRequests(t *testing.T) {
|
||||
// Channel requests on an inbound channel the local application has not
|
||||
// accepted or rejected yet are dropped.
|
||||
serverMux, clientMux := muxPair()
|
||||
defer serverMux.Close()
|
||||
defer clientMux.Close()
|
||||
go DiscardRequests(serverMux.incomingRequests)
|
||||
|
||||
// The server assigns its first inbound channel id 0; open such a channel
|
||||
// and flood it before it is decided. The channel is left undecided, as it
|
||||
// would be in the window before the server accepts or rejects it: its
|
||||
// NewChannel is never received from incomingChannels.
|
||||
ch := clientMux.newChannel("chan", channelOutbound, nil)
|
||||
if err := clientMux.sendMessage(channelOpenMsg{
|
||||
ChanType: "chan",
|
||||
PeersWindow: ch.myWindow,
|
||||
MaxPacketSize: ch.maxIncomingPayload,
|
||||
PeersID: ch.localId,
|
||||
}); err != nil {
|
||||
t.Fatalf("send open: %v", err)
|
||||
}
|
||||
for range chanSize * 4 {
|
||||
if err := clientMux.sendMessage(channelRequestMsg{PeersID: 0, Request: "x"}); err != nil {
|
||||
break // the server may have closed the connection
|
||||
}
|
||||
}
|
||||
|
||||
// The server loop keeps reading: closing the client makes it observe EOF
|
||||
// and return from Wait.
|
||||
clientMux.Close()
|
||||
serverMux.Wait()
|
||||
}
|
||||
|
||||
func TestChannelRejectedInboundDropsTraffic(t *testing.T) {
|
||||
// A rejected channel is decided but is never established, and traffic on it
|
||||
// is dropped.
|
||||
a, b := memPipe()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
go func() {
|
||||
for {
|
||||
if _, err := b.readPacket(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
m := newMux(a)
|
||||
ch := m.newChannel("session", channelInbound, nil)
|
||||
ch.remoteId = 42
|
||||
|
||||
if err := ch.Reject(ConnectionFailed, "no thanks"); err != nil {
|
||||
t.Fatalf("Reject: %v", err)
|
||||
}
|
||||
if !ch.decided {
|
||||
t.Fatal("a rejected channel should be decided")
|
||||
}
|
||||
if ch.established.Load() {
|
||||
t.Fatal("a rejected channel should not be established")
|
||||
}
|
||||
|
||||
// Flooding channel requests must not block.
|
||||
reqPacket := Marshal(channelRequestMsg{PeersID: ch.localId, Request: "x"})
|
||||
for range chanSize * 4 {
|
||||
if err := ch.handlePacket(reqPacket); err != nil {
|
||||
t.Fatalf("handlePacket(channelRequest): %v", err)
|
||||
}
|
||||
}
|
||||
if n := len(ch.incomingRequests); n != 0 {
|
||||
t.Fatalf("incomingRequests should be empty, has %d entries", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelBogusMessageTearsDownMux(t *testing.T) {
|
||||
// A message that is well-formed on the wire but is not a type expected on
|
||||
// an established channel is a protocol error that tears the mux down.
|
||||
clientMux, serverMux := muxPair()
|
||||
defer serverMux.Close()
|
||||
defer clientMux.Close()
|
||||
|
||||
serverRes := make(chan *channel, 1)
|
||||
go func() {
|
||||
newCh, ok := <-serverMux.incomingChannels
|
||||
if !ok {
|
||||
close(serverRes)
|
||||
return
|
||||
}
|
||||
c, _, err := newCh.Accept()
|
||||
if err != nil {
|
||||
close(serverRes)
|
||||
return
|
||||
}
|
||||
serverRes <- c.(*channel)
|
||||
}()
|
||||
|
||||
if _, err := clientMux.openChannel("chan", nil); err != nil {
|
||||
t.Fatalf("openChannel: %v", err)
|
||||
}
|
||||
serverCh := <-serverRes
|
||||
if serverCh == nil {
|
||||
t.Fatal("server did not accept channel")
|
||||
}
|
||||
|
||||
// Craft a packet that the client mux routes to clientCh (bytes 1:5 hold the
|
||||
// peer's channel id) and that decode accepts, but whose type is unexpected
|
||||
// on a channel. The service-string length equals the channel id, so the
|
||||
// same four bytes serve both as the routing id and as the string length.
|
||||
id := serverCh.remoteId
|
||||
packet := []byte{msgServiceRequest, 0, 0, 0, 0}
|
||||
binary.BigEndian.PutUint32(packet[1:], id)
|
||||
packet = append(packet, make([]byte, id)...)
|
||||
if err := serverMux.conn.writePacket(packet); err != nil {
|
||||
t.Fatalf("writePacket: %v", err)
|
||||
}
|
||||
|
||||
// The mux loop exits with the protocol error.
|
||||
if err := clientMux.Wait(); err == nil || !strings.Contains(err.Error(), "unexpected message type") {
|
||||
t.Fatalf("mux error = %v, want unexpected message type", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelCloseIdempotent(t *testing.T) {
|
||||
a, b := memPipe()
|
||||
defer a.Close()
|
||||
|
||||
+9
-3
@@ -331,13 +331,19 @@ func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err er
|
||||
// chars
|
||||
const maxVersionStringBytes = 255
|
||||
|
||||
// maxPreVersionLines is the maximum number of lines sent by the peer
|
||||
// before the version string. Each of these lines is limited to a maximum
|
||||
// of maxVersionStringBytes chars. Lines sent before the version string
|
||||
// are silently ignored.
|
||||
const maxPreVersionLines = 1024
|
||||
|
||||
// Read version string as specified by RFC 4253, section 4.2.
|
||||
func readVersion(r io.Reader) ([]byte, error) {
|
||||
versionString := make([]byte, 0, 64)
|
||||
var ok bool
|
||||
var buf [1]byte
|
||||
|
||||
for length := 0; length < maxVersionStringBytes; length++ {
|
||||
for lines := 0; len(versionString) < maxVersionStringBytes && lines < maxPreVersionLines; {
|
||||
_, err := io.ReadFull(r, buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -347,9 +353,9 @@ func readVersion(r io.Reader) ([]byte, error) {
|
||||
if buf[0] == '\n' {
|
||||
if !bytes.HasPrefix(versionString, []byte("SSH-")) {
|
||||
// RFC 4253 says we need to ignore all version string lines
|
||||
// except the one containing the SSH version (provided that
|
||||
// all the lines do not exceed 255 bytes in total).
|
||||
// except the one containing the SSH version.
|
||||
versionString = versionString[:0]
|
||||
lines++
|
||||
continue
|
||||
}
|
||||
ok = true
|
||||
|
||||
+16
-4
@@ -35,11 +35,15 @@ func TestReadVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadVersionError(t *testing.T) {
|
||||
longVersion := strings.Repeat("SSH-2.0-bla", 50)[:253]
|
||||
multiLineVersion := strings.Repeat("ignored\r\n", 50) + "SSH-2.0-bla\r\n"
|
||||
tooLongVersion := strings.Repeat("SSH-2.0-bla", 60) + "\r\n"
|
||||
multiLine := strings.Repeat("ignored\r\n", 50)
|
||||
tooManyLines := strings.Repeat("ignored\r\n", 2000)
|
||||
tooLongLine := strings.Repeat("blah ", 100) + "\r\n"
|
||||
cases := []string{
|
||||
longVersion + "too-long\r\n",
|
||||
multiLineVersion,
|
||||
tooLongVersion,
|
||||
multiLine + tooLongVersion,
|
||||
tooManyLines + "SSH-2.0-bla\r\n",
|
||||
tooLongLine + "SSH-2.0-bla\r\n",
|
||||
}
|
||||
for _, in := range cases {
|
||||
if _, err := readVersion(bytes.NewBufferString(in)); err == nil {
|
||||
@@ -48,6 +52,14 @@ func TestReadVersionError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnorePreVersion(t *testing.T) {
|
||||
longishLine := strings.Repeat("blah ", 30) + "\r\n"
|
||||
multiLineVersion := strings.Repeat(longishLine, 100) + "SSH-2.0-bla\r\n"
|
||||
if _, err := readVersion(bytes.NewBufferString(multiLineVersion)); err != nil {
|
||||
t.Errorf("readVersion() got error %v, expected success", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeVersionsBasic(t *testing.T) {
|
||||
v := "SSH-2.0-bla"
|
||||
buf := bytes.NewBufferString(v + "\r\n")
|
||||
|
||||
@@ -4,7 +4,7 @@ package bundle
|
||||
|
||||
var unparsedCertificates = []unparsedCertificate{
|
||||
{
|
||||
cn: "CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS,OU=Ceres,O=FNMT-RCM,C=ES,2.5.4.97=#130f56415445532d51323832363030344a",
|
||||
cn: "CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS,OU=Ceres,O=FNMT-RCM,C=ES,2.5.4.97=VATES-Q2826004J",
|
||||
sha256Hash: "554153b13d2cf9ddb753bfbe1a4e0ae08d0aa4187058fe60a2b862b2e4b87bcb",
|
||||
certStartOff: 0,
|
||||
certLength: 626,
|
||||
@@ -358,13 +358,14 @@ var unparsedCertificates = []unparsedCertificate{
|
||||
certLength: 1386,
|
||||
},
|
||||
{
|
||||
cn: "CN=Izenpe.com,O=IZENPE S.A.,C=ES",
|
||||
sha256Hash: "2530cc8e98321502bad96f9b1fba1b099e2d299e0f4548bb914f363bc0d4531f",
|
||||
certStartOff: 63236,
|
||||
certLength: 1525,
|
||||
cn: "CN=Izenpe.com,O=IZENPE S.A.,C=ES",
|
||||
sha256Hash: "2530cc8e98321502bad96f9b1fba1b099e2d299e0f4548bb914f363bc0d4531f",
|
||||
certStartOff: 63236,
|
||||
certLength: 1525,
|
||||
distrustAfter: "2026-04-15T23:59:59Z",
|
||||
},
|
||||
{
|
||||
cn: "CN=Microsec e-Szigno Root CA 2009,O=Microsec Ltd.,L=Budapest,C=HU,1.2.840.113549.1.9.1=#0c10696e666f40652d737a69676e6f2e6875",
|
||||
cn: "CN=Microsec e-Szigno Root CA 2009,O=Microsec Ltd.,L=Budapest,C=HU,1.2.840.113549.1.9.1=info@e-szigno.hu",
|
||||
sha256Hash: "3c5f81fea5fab82c64bfa2eaecafcde8e077fc8620a7cae537163df36edbf378",
|
||||
certStartOff: 64761,
|
||||
certLength: 1038,
|
||||
@@ -652,13 +653,13 @@ var unparsedCertificates = []unparsedCertificate{
|
||||
certLength: 1506,
|
||||
},
|
||||
{
|
||||
cn: "CN=e-Szigno Root CA 2017,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=#130e56415448552d3233353834343937",
|
||||
cn: "CN=e-Szigno Root CA 2017,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=VATHU-23584497",
|
||||
sha256Hash: "beb00b30839b9bc32c32e4447905950641f26421b15ed089198b518ae2ea1b99",
|
||||
certStartOff: 116668,
|
||||
certLength: 580,
|
||||
},
|
||||
{
|
||||
cn: "CN=e-Szigno TLS Root CA 2023,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=#130e56415448552d3233353834343937",
|
||||
cn: "CN=e-Szigno TLS Root CA 2023,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=VATHU-23584497",
|
||||
sha256Hash: "b49141502d00663d740f2e7ec340c52800962666121a36d09cf7dd2b90384fb4",
|
||||
certStartOff: 117248,
|
||||
certLength: 723,
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2026 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.27
|
||||
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// subjectsEqual reports whether two RFC 2253 DN strings match.
|
||||
//
|
||||
// When testing with Go 1.27 or newer, it requires subjects to match exactly.
|
||||
// When testing with Go 1.26, it tolerates the rendering difference introduced in Go 1.27,
|
||||
// where string-typed attribute values for OIDs outside attributeTypeNames are rendered
|
||||
// as strings rather than hex-encoded DER (see Go CL 773800).
|
||||
//
|
||||
// When Go 1.26 is no longer supported,
|
||||
// the Go 1.26 version can be removed and
|
||||
// the Go 1.27+ version can be inlined.
|
||||
func subjectsEqual(a, b string) bool {
|
||||
return a == b || normalizeHexValues(a) == normalizeHexValues(b)
|
||||
}
|
||||
|
||||
// normalizeHexValues rewrites any "oid=#hex" to the equivalent "oid=value"
|
||||
// rendering produced by Go 1.27+.
|
||||
func normalizeHexValues(s string) string {
|
||||
return hexAttrRE.ReplaceAllStringFunc(s, func(match string) string {
|
||||
m := hexAttrRE.FindStringSubmatch(match)
|
||||
der, err := hex.DecodeString(m[2])
|
||||
if err != nil {
|
||||
return match
|
||||
}
|
||||
var v string
|
||||
if rest, err := asn1.Unmarshal(der, &v); err != nil || len(rest) != 0 {
|
||||
return match
|
||||
}
|
||||
return m[1] + "=" + v
|
||||
})
|
||||
}
|
||||
|
||||
// hexAttrRE matches a "oid=#hex" attribute value in an RFC 2253 DN string.
|
||||
var hexAttrRE = regexp.MustCompile(`([\d.]+)=#([[:xdigit:]]+)`)
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2026 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.27
|
||||
|
||||
package bundle
|
||||
|
||||
// subjectsEqual reports whether two RFC 2253 DN strings match.
|
||||
//
|
||||
// When testing with Go 1.27 or newer, it requires subjects to match exactly.
|
||||
// When testing with Go 1.26, it tolerates the rendering difference introduced in Go 1.27,
|
||||
// where string-typed attribute values for OIDs outside attributeTypeNames are rendered
|
||||
// as strings rather than hex-encoded DER (see Go CL 773800).
|
||||
//
|
||||
// When Go 1.26 is no longer supported,
|
||||
// the Go 1.26 version can be removed and
|
||||
// the Go 1.27+ version can be inlined.
|
||||
func subjectsEqual(a, b string) bool {
|
||||
return a == b
|
||||
}
|
||||
@@ -7,9 +7,7 @@ package bundle
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -32,34 +30,3 @@ func TestBundle(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// subjectsEqual reports whether two RFC 2253 DN strings match.
|
||||
//
|
||||
// It tolerates the rendering difference introduced in Go 1.27, where
|
||||
// string-typed attribute values for OIDs outside attributeTypeNames are
|
||||
// rendered as strings rather than hex-encoded DER (see Go CL 773800).
|
||||
//
|
||||
// This can be removed when Go 1.25/1.26 are no longer supported.
|
||||
func subjectsEqual(a, b string) bool {
|
||||
return a == b || normalizeHexValues(a) == normalizeHexValues(b)
|
||||
}
|
||||
|
||||
// normalizeHexValues rewrites any "oid=#hex" to the equivalent "oid=value"
|
||||
// rendering produced by Go 1.27+.
|
||||
func normalizeHexValues(s string) string {
|
||||
return hexAttrRE.ReplaceAllStringFunc(s, func(match string) string {
|
||||
m := hexAttrRE.FindStringSubmatch(match)
|
||||
der, err := hex.DecodeString(m[2])
|
||||
if err != nil {
|
||||
return match
|
||||
}
|
||||
var v string
|
||||
if rest, err := asn1.Unmarshal(der, &v); err != nil || len(rest) != 0 {
|
||||
return match
|
||||
}
|
||||
return m[1] + "=" + v
|
||||
})
|
||||
}
|
||||
|
||||
// hexAttrRE matches a "oid=#hex" attribute value in an RFC 2253 DN string.
|
||||
var hexAttrRE = regexp.MustCompile(`([\d.]+)=#([[:xdigit:]]+)`)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module golang.org/x/crypto/x509roots/fallback
|
||||
|
||||
go 1.25.0
|
||||
go 1.26.0
|
||||
|
||||
Reference in New Issue
Block a user