Compare commits

..
Author SHA1 Message Date
umlublin cf75a17258 [internal-branch.go1.17-vendor] cryptobyte: fix parsing of large ASN.1 OIDs
Updates golang/go#49678
For golang/go#50165

Change-Id: If8a40e25edd810a66165ab78dd68d9b7fc2699f8
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/365674
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Trust: Alex Rakoczy <alex@golang.org>
Trust: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
(cherry picked from commit e495a2d5b3)
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/372274
Trust: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2021-12-15 16:50:25 +00:00
267 changed files with 16538 additions and 50324 deletions
+3
View File
@@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at https://tip.golang.org/AUTHORS.
+3
View File
@@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at https://tip.golang.org/CONTRIBUTORS.
+2 -2
View File
@@ -1,4 +1,4 @@
Copyright 2009 The Go Authors.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
+375 -91
View File
@@ -3,20 +3,17 @@
// license that can be found in the LICENSE file.
// Package acme provides an implementation of the
// Automatic Certificate Management Environment (ACME) spec,
// most famously used by Let's Encrypt.
//
// The initial implementation of this package was based on an early version
// of the spec. The current implementation supports only the modern
// RFC 8555 but some of the old API surface remains for compatibility.
// While code using the old API will still compile, it will return an error.
// Note the deprecation comments to update your code.
//
// See https://tools.ietf.org/html/rfc8555 for the spec.
// Automatic Certificate Management Environment (ACME) spec.
// The intial implementation was based on ACME draft-02 and
// is now being extended to comply with RFC 8555.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02
// and https://tools.ietf.org/html/rfc8555 for details.
//
// Most common scenarios will want to use autocert subdirectory instead,
// which provides automatic access to certificates from Let's Encrypt
// and any other ACME-based CA.
//
// This package is a work in progress and makes no API stability promises.
package acme
import (
@@ -36,6 +33,8 @@ import (
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"strings"
@@ -73,22 +72,22 @@ const (
)
// Client is an ACME client.
//
// The only required field is Key. An example of creating a client with a new key
// is as follows:
//
// key, err := rsa.GenerateKey(rand.Reader, 2048)
// if err != nil {
// log.Fatal(err)
// }
// client := &Client{Key: key}
// key, err := rsa.GenerateKey(rand.Reader, 2048)
// if err != nil {
// log.Fatal(err)
// }
// client := &Client{Key: key}
//
type Client struct {
// Key is the account key used to register with a CA and sign requests.
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
//
// The following algorithms are supported:
// RS256, ES256, ES384 and ES512.
// See RFC 7518 for more details about the algorithms.
// See RFC7518 for more details about the algorithms.
Key crypto.Signer
// HTTPClient optionally specifies an HTTP client to use
@@ -126,9 +125,7 @@ type Client struct {
cacheMu sync.Mutex
dir *Directory // cached result of Client's Discover method
// KID is the key identifier provided by the CA. If not provided it will be
// retrieved from the CA by making a call to the registration endpoint.
KID KeyID
kid keyID // cached Account.URI obtained from registerRFC or getAccountRFC
noncesMu sync.Mutex
nonces map[string]struct{} // nonces collected from previous responses
@@ -143,22 +140,23 @@ type Client struct {
//
// When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID
// returns noKeyID.
func (c *Client) accountKID(ctx context.Context) KeyID {
func (c *Client) accountKID(ctx context.Context) keyID {
c.cacheMu.Lock()
defer c.cacheMu.Unlock()
if c.KID != noKeyID {
return c.KID
if !c.dir.rfcCompliant() {
return noKeyID
}
if c.kid != noKeyID {
return c.kid
}
a, err := c.getRegRFC(ctx)
if err != nil {
return noKeyID
}
c.KID = KeyID(a.URI)
return c.KID
c.kid = keyID(a.URI)
return c.kid
}
var errPreRFC = errors.New("acme: server does not support the RFC 8555 version of ACME")
// Discover performs ACME server discovery using c.DirectoryURL.
//
// It caches successful result. So, subsequent calls will not result in
@@ -179,36 +177,53 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
c.addNonce(res.Header)
var v struct {
Reg string `json:"newAccount"`
Authz string `json:"newAuthz"`
Order string `json:"newOrder"`
Revoke string `json:"revokeCert"`
Nonce string `json:"newNonce"`
KeyChange string `json:"keyChange"`
Meta struct {
Terms string `json:"termsOfService"`
Website string `json:"website"`
CAA []string `json:"caaIdentities"`
ExternalAcct bool `json:"externalAccountRequired"`
Reg string `json:"new-reg"`
RegRFC string `json:"newAccount"`
Authz string `json:"new-authz"`
AuthzRFC string `json:"newAuthz"`
OrderRFC string `json:"newOrder"`
Cert string `json:"new-cert"`
Revoke string `json:"revoke-cert"`
RevokeRFC string `json:"revokeCert"`
NonceRFC string `json:"newNonce"`
KeyChangeRFC string `json:"keyChange"`
Meta struct {
Terms string `json:"terms-of-service"`
TermsRFC string `json:"termsOfService"`
WebsiteRFC string `json:"website"`
CAA []string `json:"caa-identities"`
CAARFC []string `json:"caaIdentities"`
ExternalAcctRFC bool `json:"externalAccountRequired"`
}
}
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
return Directory{}, err
}
if v.Order == "" {
return Directory{}, errPreRFC
if v.OrderRFC == "" {
// Non-RFC compliant ACME CA.
c.dir = &Directory{
RegURL: v.Reg,
AuthzURL: v.Authz,
CertURL: v.Cert,
RevokeURL: v.Revoke,
Terms: v.Meta.Terms,
Website: v.Meta.WebsiteRFC,
CAA: v.Meta.CAA,
}
return *c.dir, nil
}
// RFC compliant ACME CA.
c.dir = &Directory{
RegURL: v.Reg,
AuthzURL: v.Authz,
OrderURL: v.Order,
RevokeURL: v.Revoke,
NonceURL: v.Nonce,
KeyChangeURL: v.KeyChange,
Terms: v.Meta.Terms,
Website: v.Meta.Website,
CAA: v.Meta.CAA,
ExternalAccountRequired: v.Meta.ExternalAcct,
RegURL: v.RegRFC,
AuthzURL: v.AuthzRFC,
OrderURL: v.OrderRFC,
RevokeURL: v.RevokeRFC,
NonceURL: v.NonceRFC,
KeyChangeURL: v.KeyChangeRFC,
Terms: v.Meta.TermsRFC,
Website: v.Meta.WebsiteRFC,
CAA: v.Meta.CAARFC,
ExternalAccountRequired: v.Meta.ExternalAcctRFC,
}
return *c.dir, nil
}
@@ -220,11 +235,55 @@ func (c *Client) directoryURL() string {
return LetsEncryptURL
}
// CreateCert was part of the old version of ACME. It is incompatible with RFC 8555.
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
// It is incompatible with RFC 8555. Callers should use CreateOrderCert when interfacing
// with an RFC-compliant CA.
//
// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateOrderCert.
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
// with a different duration.
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
//
// In the case where CA server does not provide the issued certificate in the response,
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
// In such a scenario, the caller can cancel the polling with ctx.
//
// CreateCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
return nil, "", errPreRFC
if _, err := c.Discover(ctx); err != nil {
return nil, "", err
}
req := struct {
Resource string `json:"resource"`
CSR string `json:"csr"`
NotBefore string `json:"notBefore,omitempty"`
NotAfter string `json:"notAfter,omitempty"`
}{
Resource: "new-cert",
CSR: base64.RawURLEncoding.EncodeToString(csr),
}
now := timeNow()
req.NotBefore = now.Format(time.RFC3339)
if exp > 0 {
req.NotAfter = now.Add(exp).Format(time.RFC3339)
}
res, err := c.post(ctx, nil, c.dir.CertURL, req, wantStatus(http.StatusCreated))
if err != nil {
return nil, "", err
}
defer res.Body.Close()
curl := res.Header.Get("Location") // cert permanent URL
if res.ContentLength == 0 {
// no cert in the body; poll until we get it
cert, err := c.FetchCert(ctx, curl, bundle)
return cert, curl, err
}
// slurp issued cert and CA chain, if requested
cert, err := c.responseCert(ctx, res, bundle)
return cert, curl, err
}
// FetchCert retrieves already issued certificate from the given url, in DER format.
@@ -238,10 +297,20 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
// Callers are encouraged to parse the returned value to ensure the certificate is valid
// and has expected features.
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
if _, err := c.Discover(ctx); err != nil {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
return c.fetchCertRFC(ctx, url, bundle)
if dir.rfcCompliant() {
return c.fetchCertRFC(ctx, url, bundle)
}
// Legacy non-authenticated GET request.
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
if err != nil {
return nil, err
}
return c.responseCert(ctx, res, bundle)
}
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
@@ -251,10 +320,30 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by
// For instance, the key pair of the certificate may be authorized.
// If the key is nil, c.Key is used instead.
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
if _, err := c.Discover(ctx); err != nil {
dir, err := c.Discover(ctx)
if err != nil {
return err
}
return c.revokeCertRFC(ctx, key, cert, reason)
if dir.rfcCompliant() {
return c.revokeCertRFC(ctx, key, cert, reason)
}
// Legacy CA.
body := &struct {
Resource string `json:"resource"`
Cert string `json:"certificate"`
Reason int `json:"reason"`
}{
Resource: "revoke-cert",
Cert: base64.RawURLEncoding.EncodeToString(cert),
Reason: int(reason),
}
res, err := c.post(ctx, key, dir.RevokeURL, body, wantStatus(http.StatusOK))
if err != nil {
return err
}
defer res.Body.Close()
return nil
}
// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
@@ -277,47 +366,75 @@ func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL
if c.Key == nil {
return nil, errors.New("acme: client.Key must be set to Register")
}
if _, err := c.Discover(ctx); err != nil {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
return c.registerRFC(ctx, acct, prompt)
if dir.rfcCompliant() {
return c.registerRFC(ctx, acct, prompt)
}
// Legacy ACME draft registration flow.
a, err := c.doReg(ctx, dir.RegURL, "new-reg", acct)
if err != nil {
return nil, err
}
var accept bool
if a.CurrentTerms != "" && a.CurrentTerms != a.AgreedTerms {
accept = prompt(a.CurrentTerms)
}
if accept {
a.AgreedTerms = a.CurrentTerms
a, err = c.UpdateReg(ctx, a)
}
return a, err
}
// GetReg retrieves an existing account associated with c.Key.
//
// The url argument is a legacy artifact of the pre-RFC 8555 API
// and is ignored.
// The url argument is an Account URI used with pre-RFC 8555 CAs.
// It is ignored when interfacing with an RFC-compliant CA.
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
if _, err := c.Discover(ctx); err != nil {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
return c.getRegRFC(ctx)
if dir.rfcCompliant() {
return c.getRegRFC(ctx)
}
// Legacy CA.
a, err := c.doReg(ctx, url, "reg", nil)
if err != nil {
return nil, err
}
a.URI = url
return a, nil
}
// UpdateReg updates an existing registration.
// It returns an updated account copy. The provided account is not modified.
//
// The account's URI is ignored and the account URL associated with
// c.Key is used instead.
// When interfacing with RFC-compliant CAs, a.URI is ignored and the account URL
// associated with c.Key is used instead.
func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) {
if _, err := c.Discover(ctx); err != nil {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
return c.updateRegRFC(ctx, acct)
}
if dir.rfcCompliant() {
return c.updateRegRFC(ctx, acct)
}
// AccountKeyRollover attempts to transition a client's account key to a new key.
// On success client's Key is updated which is not concurrency safe.
// On failure an error will be returned.
// The new key is already registered with the ACME provider if the following is true:
// - error is of type acme.Error
// - StatusCode should be 409 (Conflict)
// - Location header will have the KID of the associated account
//
// More about account key rollover can be found at
// https://tools.ietf.org/html/rfc8555#section-7.3.5.
func (c *Client) AccountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
return c.accountKeyRollover(ctx, newKey)
// Legacy CA.
uri := acct.URI
a, err := c.doReg(ctx, uri, "reg", acct)
if err != nil {
return nil, err
}
a.URI = uri
return a, nil
}
// Authorize performs the initial step in the pre-authorization flow,
@@ -386,11 +503,17 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization
// If a caller needs to poll an authorization until its status is final,
// see the WaitAuthorization method.
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
if _, err := c.Discover(ctx); err != nil {
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
var res *http.Response
if dir.rfcCompliant() {
res, err = c.postAsGet(ctx, url, wantStatus(http.StatusOK))
} else {
res, err = c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
}
if err != nil {
return nil, err
}
@@ -412,6 +535,7 @@ func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorizati
//
// It does not revoke existing certificates.
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
// Required for c.accountKID() when in RFC mode.
if _, err := c.Discover(ctx); err != nil {
return err
}
@@ -441,11 +565,18 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
// In all other cases WaitAuthorization returns an error.
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
if _, err := c.Discover(ctx); err != nil {
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
getfn := c.postAsGet
if !dir.rfcCompliant() {
getfn = c.get
}
for {
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
if err != nil {
return nil, err
}
@@ -488,11 +619,17 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
//
// A client typically polls a challenge status using this method.
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
if _, err := c.Discover(ctx); err != nil {
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
getfn := c.postAsGet
if !dir.rfcCompliant() {
getfn = c.get
}
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
if err != nil {
return nil, err
}
@@ -510,11 +647,29 @@ func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, erro
//
// The server will then perform the validation asynchronously.
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
if _, err := c.Discover(ctx); err != nil {
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
return nil, err
}
res, err := c.post(ctx, nil, chal.URI, json.RawMessage("{}"), wantStatus(
var req interface{} = json.RawMessage("{}") // RFC-compliant CA
if !dir.rfcCompliant() {
auth, err := keyAuth(c.Key.Public(), chal.Token)
if err != nil {
return nil, err
}
req = struct {
Resource string `json:"resource"`
Type string `json:"type"`
Auth string `json:"keyAuthorization"`
}{
Resource: "challenge",
Type: chal.Type,
Auth: auth,
}
}
res, err := c.post(ctx, nil, chal.URI, req, wantStatus(
http.StatusOK, // according to the spec
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
))
@@ -565,7 +720,7 @@ func (c *Client) HTTP01ChallengePath(token string) string {
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
//
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
ka, err := keyAuth(c.Key.Public(), token)
if err != nil {
@@ -583,7 +738,7 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
//
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
b := sha256.Sum256([]byte(token))
h := hex.EncodeToString(b[:])
@@ -650,6 +805,63 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
return tlsChallengeCert([]string{domain}, newOpt)
}
// doReg sends all types of registration requests the old way (pre-RFC world).
// The type of request is identified by typ argument, which is a "resource"
// in the ACME spec terms.
//
// A non-nil acct argument indicates whether the intention is to mutate data
// of the Account. Only Contact and Agreement of its fields are used
// in such cases.
func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Account) (*Account, error) {
req := struct {
Resource string `json:"resource"`
Contact []string `json:"contact,omitempty"`
Agreement string `json:"agreement,omitempty"`
}{
Resource: typ,
}
if acct != nil {
req.Contact = acct.Contact
req.Agreement = acct.AgreedTerms
}
res, err := c.post(ctx, nil, url, req, wantStatus(
http.StatusOK, // updates and deletes
http.StatusCreated, // new account creation
http.StatusAccepted, // Let's Encrypt divergent implementation
))
if err != nil {
return nil, err
}
defer res.Body.Close()
var v struct {
Contact []string
Agreement string
Authorizations string
Certificates string
}
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
return nil, fmt.Errorf("acme: invalid response: %v", err)
}
var tos string
if v := linkHeader(res.Header, "terms-of-service"); len(v) > 0 {
tos = v[0]
}
var authz string
if v := linkHeader(res.Header, "next"); len(v) > 0 {
authz = v[0]
}
return &Account{
URI: res.Header.Get("Location"),
Contact: v.Contact,
AgreedTerms: v.Agreement,
CurrentTerms: tos,
Authz: authz,
Authorizations: v.Authorizations,
Certificates: v.Certificates,
}, nil
}
// popNonce returns a nonce value previously stored with c.addNonce
// or fetches a fresh one from c.dir.NonceURL.
// If NonceURL is empty, it first tries c.directoryURL() and, failing that,
@@ -724,6 +936,78 @@ func nonceFromHeader(h http.Header) string {
return h.Get("Replay-Nonce")
}
func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) {
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
if err != nil {
return nil, fmt.Errorf("acme: response stream: %v", err)
}
if len(b) > maxCertSize {
return nil, errors.New("acme: certificate is too big")
}
cert := [][]byte{b}
if !bundle {
return cert, nil
}
// Append CA chain cert(s).
// At least one is required according to the spec:
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1
up := linkHeader(res.Header, "up")
if len(up) == 0 {
return nil, errors.New("acme: rel=up link not found")
}
if len(up) > maxChainLen {
return nil, errors.New("acme: rel=up link is too large")
}
for _, url := range up {
cc, err := c.chainCert(ctx, url, 0)
if err != nil {
return nil, err
}
cert = append(cert, cc...)
}
return cert, nil
}
// chainCert fetches CA certificate chain recursively by following "up" links.
// Each recursive call increments the depth by 1, resulting in an error
// if the recursion level reaches maxChainLen.
//
// First chainCert call starts with depth of 0.
func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) {
if depth >= maxChainLen {
return nil, errors.New("acme: certificate chain is too deep")
}
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
if err != nil {
return nil, err
}
defer res.Body.Close()
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
if err != nil {
return nil, err
}
if len(b) > maxCertSize {
return nil, errors.New("acme: certificate is too big")
}
chain := [][]byte{b}
uplink := linkHeader(res.Header, "up")
if len(uplink) > maxChainLen {
return nil, errors.New("acme: certificate chain is too large")
}
for _, up := range uplink {
cc, err := c.chainCert(ctx, up, depth+1)
if err != nil {
return nil, err
}
chain = append(chain, cc...)
}
return chain, nil
}
// linkHeader returns URI-Reference values of all Link headers
// with relation-type rel.
// See https://tools.ietf.org/html/rfc5988#section-5 for details.
@@ -814,5 +1098,5 @@ func encodePEM(typ string, b []byte) []byte {
return pem.EncodeToMemory(pb)
}
// timeNow is time.Now, except in tests which can mess with it.
// timeNow is useful for testing for fixed current time.
var timeNow = time.Now
+656 -15
View File
@@ -79,6 +79,115 @@ func decodeJWSHead(r io.Reader) (*jwsHead, error) {
return &head, nil
}
func TestDiscover(t *testing.T) {
const (
reg = "https://example.com/acme/new-reg"
authz = "https://example.com/acme/new-authz"
cert = "https://example.com/acme/new-cert"
revoke = "https://example.com/acme/revoke-cert"
)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Replay-Nonce", "testnonce")
fmt.Fprintf(w, `{
"new-reg": %q,
"new-authz": %q,
"new-cert": %q,
"revoke-cert": %q
}`, reg, authz, cert, revoke)
}))
defer ts.Close()
c := Client{DirectoryURL: ts.URL}
dir, err := c.Discover(context.Background())
if err != nil {
t.Fatal(err)
}
if dir.RegURL != reg {
t.Errorf("dir.RegURL = %q; want %q", dir.RegURL, reg)
}
if dir.AuthzURL != authz {
t.Errorf("dir.AuthzURL = %q; want %q", dir.AuthzURL, authz)
}
if dir.CertURL != cert {
t.Errorf("dir.CertURL = %q; want %q", dir.CertURL, cert)
}
if dir.RevokeURL != revoke {
t.Errorf("dir.RevokeURL = %q; want %q", dir.RevokeURL, revoke)
}
if _, exist := c.nonces["testnonce"]; !exist {
t.Errorf("c.nonces = %q; want 'testnonce' in the map", c.nonces)
}
}
func TestRegister(t *testing.T) {
contacts := []string{"mailto:admin@example.com"}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Contact []string
Agreement string
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "new-reg" {
t.Errorf("j.Resource = %q; want new-reg", j.Resource)
}
if !reflect.DeepEqual(j.Contact, contacts) {
t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
}
w.Header().Set("Location", "https://ca.tld/acme/reg/1")
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
w.Header().Add("Link", `<https://ca.tld/acme/terms>;rel="terms-of-service"`)
w.WriteHeader(http.StatusCreated)
b, _ := json.Marshal(contacts)
fmt.Fprintf(w, `{"contact": %s}`, b)
}))
defer ts.Close()
prompt := func(url string) bool {
const terms = "https://ca.tld/acme/terms"
if url != terms {
t.Errorf("prompt url = %q; want %q", url, terms)
}
return false
}
c := Client{
Key: testKeyEC,
DirectoryURL: ts.URL,
dir: &Directory{RegURL: ts.URL},
}
a := &Account{Contact: contacts}
var err error
if a, err = c.Register(context.Background(), a, prompt); err != nil {
t.Fatal(err)
}
if a.URI != "https://ca.tld/acme/reg/1" {
t.Errorf("a.URI = %q; want https://ca.tld/acme/reg/1", a.URI)
}
if a.Authz != "https://ca.tld/acme/new-authz" {
t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
}
if a.CurrentTerms != "https://ca.tld/acme/terms" {
t.Errorf("a.CurrentTerms = %q; want https://ca.tld/acme/terms", a.CurrentTerms)
}
if !reflect.DeepEqual(a.Contact, contacts) {
t.Errorf("a.Contact = %v; want %v", a.Contact, contacts)
}
}
func TestRegisterWithoutKey(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
@@ -104,6 +213,134 @@ func TestRegisterWithoutKey(t *testing.T) {
}
}
func TestUpdateReg(t *testing.T) {
const terms = "https://ca.tld/acme/terms"
contacts := []string{"mailto:admin@example.com"}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Contact []string
Agreement string
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "reg" {
t.Errorf("j.Resource = %q; want reg", j.Resource)
}
if j.Agreement != terms {
t.Errorf("j.Agreement = %q; want %q", j.Agreement, terms)
}
if !reflect.DeepEqual(j.Contact, contacts) {
t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
}
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, terms))
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(contacts)
fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
}))
defer ts.Close()
c := Client{
Key: testKeyEC,
DirectoryURL: ts.URL, // don't dial outside of localhost
dir: &Directory{}, // don't do discovery
}
a := &Account{URI: ts.URL, Contact: contacts, AgreedTerms: terms}
var err error
if a, err = c.UpdateReg(context.Background(), a); err != nil {
t.Fatal(err)
}
if a.Authz != "https://ca.tld/acme/new-authz" {
t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
}
if a.AgreedTerms != terms {
t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
}
if a.CurrentTerms != terms {
t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, terms)
}
if a.URI != ts.URL {
t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
}
}
func TestGetReg(t *testing.T) {
const terms = "https://ca.tld/acme/terms"
const newTerms = "https://ca.tld/acme/new-terms"
contacts := []string{"mailto:admin@example.com"}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Contact []string
Agreement string
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "reg" {
t.Errorf("j.Resource = %q; want reg", j.Resource)
}
if len(j.Contact) != 0 {
t.Errorf("j.Contact = %v", j.Contact)
}
if j.Agreement != "" {
t.Errorf("j.Agreement = %q", j.Agreement)
}
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, newTerms))
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(contacts)
fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
}))
defer ts.Close()
c := Client{
Key: testKeyEC,
DirectoryURL: ts.URL, // don't dial outside of localhost
dir: &Directory{}, // don't do discovery
}
a, err := c.GetReg(context.Background(), ts.URL)
if err != nil {
t.Fatal(err)
}
if a.Authz != "https://ca.tld/acme/new-authz" {
t.Errorf("a.AuthzURL = %q; want https://ca.tld/acme/new-authz", a.Authz)
}
if a.AgreedTerms != terms {
t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
}
if a.CurrentTerms != newTerms {
t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, newTerms)
}
if a.URI != ts.URL {
t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
}
}
func TestAuthorize(t *testing.T) {
tt := []struct{ typ, value string }{
{"dns", "example.com"},
@@ -254,6 +491,82 @@ func TestAuthorizeValid(t *testing.T) {
}
}
func TestGetAuthorization(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Errorf("r.Method = %q; want GET", r.Method)
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{
"identifier": {"type":"dns","value":"example.com"},
"status":"pending",
"challenges":[
{
"type":"http-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id1",
"token":"token1"
},
{
"type":"tls-sni-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id2",
"token":"token2"
}
],
"combinations":[[0],[1]]}`)
}))
defer ts.Close()
cl := Client{Key: testKeyEC, DirectoryURL: ts.URL}
auth, err := cl.GetAuthorization(context.Background(), ts.URL)
if err != nil {
t.Fatal(err)
}
if auth.Status != "pending" {
t.Errorf("Status = %q; want pending", auth.Status)
}
if auth.Identifier.Type != "dns" {
t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type)
}
if auth.Identifier.Value != "example.com" {
t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value)
}
if n := len(auth.Challenges); n != 2 {
t.Fatalf("len(set.Challenges) = %d; want 2", n)
}
c := auth.Challenges[0]
if c.Type != "http-01" {
t.Errorf("c.Type = %q; want http-01", c.Type)
}
if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
}
if c.Token != "token1" {
t.Errorf("c.Token = %q; want token1", c.Token)
}
c = auth.Challenges[1]
if c.Type != "tls-sni-01" {
t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
}
if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
}
if c.Token != "token2" {
t.Errorf("c.Token = %q; want token2", c.Token)
}
combs := [][]int{{0}, {1}}
if !reflect.DeepEqual(auth.Combinations, combs) {
t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
}
}
func TestWaitAuthorization(t *testing.T) {
t.Run("wait loop", func(t *testing.T) {
var count int
@@ -354,34 +667,38 @@ func TestWaitAuthorization(t *testing.T) {
})
}
t.Run("context cancel", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
_, err := runWaitAuthorization(ctx, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Retry-After", "60")
fmt.Fprintf(w, `{"status":"pending"}`)
time.AfterFunc(1*time.Millisecond, cancel)
})
if err == nil {
t.Error("err is nil")
}
})
}
func runWaitAuthorization(ctx context.Context, t *testing.T, h http.HandlerFunc) (*Authorization, error) {
t.Helper()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Replay-Nonce", fmt.Sprintf("bad-test-nonce-%v", time.Now().UnixNano()))
h(w, r)
}))
ts := httptest.NewServer(h)
defer ts.Close()
client := &Client{
Key: testKey,
DirectoryURL: ts.URL,
dir: &Directory{},
KID: "some-key-id", // set to avoid lookup attempt
type res struct {
authz *Authorization
err error
}
return client.WaitAuthorization(ctx, ts.URL)
ch := make(chan res, 1)
go func() {
var client = Client{DirectoryURL: ts.URL}
a, err := client.WaitAuthorization(ctx, ts.URL)
ch <- res{a, err}
}()
select {
case <-time.After(3 * time.Second):
t.Fatal("WaitAuthorization took too long to return")
case v := <-ch:
return v.authz, v.err
}
panic("runWaitAuthorization: out of select")
}
func TestRevokeAuthorization(t *testing.T) {
@@ -426,9 +743,238 @@ func TestRevokeAuthorization(t *testing.T) {
}
}
func TestPollChallenge(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Errorf("r.Method = %q; want GET", r.Method)
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{
"type":"http-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id1",
"token":"token1"}`)
}))
defer ts.Close()
cl := Client{Key: testKeyEC, DirectoryURL: ts.URL}
chall, err := cl.GetChallenge(context.Background(), ts.URL)
if err != nil {
t.Fatal(err)
}
if chall.Status != "pending" {
t.Errorf("Status = %q; want pending", chall.Status)
}
if chall.Type != "http-01" {
t.Errorf("c.Type = %q; want http-01", chall.Type)
}
if chall.URI != "https://ca.tld/acme/challenge/publickey/id1" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", chall.URI)
}
if chall.Token != "token1" {
t.Errorf("c.Token = %q; want token1", chall.Token)
}
}
func TestAcceptChallenge(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Type string
Auth string `json:"keyAuthorization"`
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "challenge" {
t.Errorf(`resource = %q; want "challenge"`, j.Resource)
}
if j.Type != "http-01" {
t.Errorf(`type = %q; want "http-01"`, j.Type)
}
keyAuth := "token1." + testKeyECThumbprint
if j.Auth != keyAuth {
t.Errorf(`keyAuthorization = %q; want %q`, j.Auth, keyAuth)
}
// Respond to request
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, `{
"type":"http-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id1",
"token":"token1",
"keyAuthorization":%q
}`, keyAuth)
}))
defer ts.Close()
cl := Client{
Key: testKeyEC,
DirectoryURL: ts.URL, // don't dial outside of localhost
dir: &Directory{}, // don't do discovery
}
c, err := cl.Accept(context.Background(), &Challenge{
URI: ts.URL,
Token: "token1",
Type: "http-01",
})
if err != nil {
t.Fatal(err)
}
if c.Type != "http-01" {
t.Errorf("c.Type = %q; want http-01", c.Type)
}
if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
}
if c.Token != "token1" {
t.Errorf("c.Token = %q; want token1", c.Token)
}
}
func TestNewCert(t *testing.T) {
notBefore := time.Now()
notAfter := notBefore.AddDate(0, 2, 0)
timeNow = func() time.Time { return notBefore }
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string `json:"resource"`
CSR string `json:"csr"`
NotBefore string `json:"notBefore,omitempty"`
NotAfter string `json:"notAfter,omitempty"`
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "new-cert" {
t.Errorf(`resource = %q; want "new-cert"`, j.Resource)
}
if j.NotBefore != notBefore.Format(time.RFC3339) {
t.Errorf(`notBefore = %q; wanted %q`, j.NotBefore, notBefore.Format(time.RFC3339))
}
if j.NotAfter != notAfter.Format(time.RFC3339) {
t.Errorf(`notAfter = %q; wanted %q`, j.NotAfter, notAfter.Format(time.RFC3339))
}
// Respond to request
template := x509.Certificate{
SerialNumber: big.NewInt(int64(1)),
Subject: pkix.Name{
Organization: []string{"goacme"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
sampleCert, err := x509.CreateCertificate(rand.Reader, &template, &template, &testKeyEC.PublicKey, testKeyEC)
if err != nil {
t.Fatalf("Error creating certificate: %v", err)
}
w.Header().Set("Location", "https://ca.tld/acme/cert/1")
w.WriteHeader(http.StatusCreated)
w.Write(sampleCert)
}))
defer ts.Close()
csr := x509.CertificateRequest{
Version: 0,
Subject: pkix.Name{
CommonName: "example.com",
Organization: []string{"goacme"},
},
}
csrb, err := x509.CreateCertificateRequest(rand.Reader, &csr, testKeyEC)
if err != nil {
t.Fatal(err)
}
c := Client{Key: testKeyEC, dir: &Directory{CertURL: ts.URL}}
cert, certURL, err := c.CreateCert(context.Background(), csrb, notAfter.Sub(notBefore), false)
if err != nil {
t.Fatal(err)
}
if cert == nil {
t.Errorf("cert is nil")
}
if certURL != "https://ca.tld/acme/cert/1" {
t.Errorf("certURL = %q; want https://ca.tld/acme/cert/1", certURL)
}
}
func TestFetchCert(t *testing.T) {
var count byte
var ts *httptest.Server
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
if count < 3 {
up := fmt.Sprintf("<%s>;rel=up", ts.URL)
w.Header().Set("Link", up)
}
w.Write([]byte{count})
}))
defer ts.Close()
cl := newTestClient()
res, err := cl.FetchCert(context.Background(), ts.URL, true)
if err != nil {
t.Fatalf("FetchCert: %v", err)
}
cert := [][]byte{{1}, {2}, {3}}
if !reflect.DeepEqual(res, cert) {
t.Errorf("res = %v; want %v", res, cert)
}
}
func TestFetchCertRetry(t *testing.T) {
var count int
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if count < 1 {
w.Header().Set("Retry-After", "0")
w.WriteHeader(http.StatusTooManyRequests)
count++
return
}
w.Write([]byte{1})
}))
defer ts.Close()
cl := newTestClient()
res, err := cl.FetchCert(context.Background(), ts.URL, false)
if err != nil {
t.Fatalf("FetchCert: %v", err)
}
cert := [][]byte{{1}}
if !reflect.DeepEqual(res, cert) {
t.Errorf("res = %v; want %v", res, cert)
}
}
func TestFetchCertCancel(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
w.Header().Set("Retry-After", "0")
w.WriteHeader(http.StatusBadRequest)
}))
@@ -497,6 +1043,42 @@ func TestFetchCertSize(t *testing.T) {
}
}
func TestRevokeCert(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "nonce")
return
}
var req struct {
Resource string
Certificate string
Reason int
}
decodeJWSRequest(t, &req, r.Body)
if req.Resource != "revoke-cert" {
t.Errorf("req.Resource = %q; want revoke-cert", req.Resource)
}
if req.Reason != 1 {
t.Errorf("req.Reason = %d; want 1", req.Reason)
}
// echo -n cert | base64 | tr -d '=' | tr '/+' '_-'
cert := "Y2VydA"
if req.Certificate != cert {
t.Errorf("req.Certificate = %q; want %q", req.Certificate, cert)
}
}))
defer ts.Close()
client := &Client{
Key: testKeyEC,
dir: &Directory{RevokeURL: ts.URL},
}
ctx := context.Background()
if err := client.RevokeCert(ctx, nil, []byte("cert"), CRLReasonKeyCompromise); err != nil {
t.Fatal(err)
}
}
func TestNonce_add(t *testing.T) {
var c Client
c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
@@ -617,6 +1199,65 @@ func TestNonce_popWhenEmpty(t *testing.T) {
}
}
func TestNonce_postJWS(t *testing.T) {
var count int
seen := make(map[string]bool)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
w.Header().Set("Replay-Nonce", fmt.Sprintf("nonce%d", count))
if r.Method == "HEAD" {
// We expect the client do a HEAD request
// but only to fetch the first nonce.
return
}
// Make client.Authorize happy; we're not testing its result.
defer func() {
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"status":"valid"}`))
}()
head, err := decodeJWSHead(r.Body)
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
return
}
if head.Nonce == "" {
t.Error("head.Nonce is empty")
return
}
if seen[head.Nonce] {
t.Errorf("nonce is already used: %q", head.Nonce)
}
seen[head.Nonce] = true
}))
defer ts.Close()
client := Client{
Key: testKey,
DirectoryURL: ts.URL, // nonces are fetched from here first
dir: &Directory{AuthzURL: ts.URL},
}
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 1: %v", err)
}
// The second call should not generate another extra HEAD request.
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 2: %v", err)
}
if count != 3 {
t.Errorf("total requests count: %d; want 3", count)
}
if n := len(client.nonces); n != 1 {
t.Errorf("len(client.nonces) = %d; want 1", n)
}
for k := range seen {
if _, exist := client.nonces[k]; exist {
t.Errorf("used nonce %q in client.nonces", k)
}
}
}
func TestLinkHeader(t *testing.T) {
h := http.Header{"Link": {
`<https://example.com/acme/new-authz>;rel="next"`,
+92 -41
View File
@@ -47,8 +47,6 @@ var createCertRetryAfter = time.Minute
// pseudoRand is safe for concurrent use.
var pseudoRand *lockedMathRand
var errPreRFC = errors.New("autocert: ACME server doesn't support RFC 8555")
func init() {
src := mathrand.NewSource(time.Now().UnixNano())
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
@@ -170,11 +168,6 @@ type Manager struct {
// in the template's ExtraExtensions field as is.
ExtraExtensions []pkix.Extension
// ExternalAccountBinding optionally represents an arbitrary binding to an
// account of the CA to which the ACME server is tied.
// See RFC 8555, Section 7.3.4 for more details.
ExternalAccountBinding *acme.ExternalAccountBinding
clientMu sync.Mutex
client *acme.Client // initialized by acmeClient method
@@ -463,7 +456,7 @@ func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error
leaf: cert.Leaf,
}
m.state[ck] = s
m.startRenew(ck, s.key, s.leaf.NotAfter)
go m.renew(ck, s.key, s.leaf.NotAfter)
return cert, nil
}
@@ -589,9 +582,8 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
if err != nil {
// Remove the failed state after some time,
// making the manager call createCert again on the following TLS hello.
didRemove := testDidRemoveState // The lifetime of this timer is untracked, so copy mutable local state to avoid races.
time.AfterFunc(createCertRetryAfter, func() {
defer didRemove(ck)
defer testDidRemoveState(ck)
m.stateMu.Lock()
defer m.stateMu.Unlock()
// Verify the state hasn't changed and it's still invalid
@@ -609,7 +601,7 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
}
state.cert = der
state.leaf = leaf
m.startRenew(ck, state.key, state.leaf.NotAfter)
go m.renew(ck, state.key, state.leaf.NotAfter)
return state.tlscert()
}
@@ -666,19 +658,31 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
if err != nil {
return nil, nil, err
}
if dir.OrderURL == "" {
return nil, nil, errPreRFC
}
o, err := m.verifyRFC(ctx, client, ck.domain)
if err != nil {
return nil, nil, err
var chain [][]byte
switch {
// Pre-RFC legacy CA.
case dir.OrderURL == "":
if err := m.verify(ctx, client, ck.domain); err != nil {
return nil, nil, err
}
der, _, err := client.CreateCert(ctx, csr, 0, true)
if err != nil {
return nil, nil, err
}
chain = der
// RFC 8555 compliant CA.
default:
o, err := m.verifyRFC(ctx, client, ck.domain)
if err != nil {
return nil, nil, err
}
der, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
if err != nil {
return nil, nil, err
}
chain = der
}
chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
if err != nil {
return nil, nil, err
}
leaf, err = validCert(ck, chain, key, m.now())
if err != nil {
return nil, nil, err
@@ -686,6 +690,69 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
return chain, leaf, nil
}
// verify runs the identifier (domain) pre-authorization flow for legacy CAs
// using each applicable ACME challenge type.
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
// Remove all hanging authorizations to reduce rate limit quotas
// after we're done.
var authzURLs []string
defer func() {
go m.deactivatePendingAuthz(authzURLs)
}()
// errs accumulates challenge failure errors, printed if all fail
errs := make(map[*acme.Challenge]error)
challengeTypes := m.supportedChallengeTypes()
var nextTyp int // challengeType index of the next challenge type to try
for {
// Start domain authorization and get the challenge.
authz, err := client.Authorize(ctx, domain)
if err != nil {
return err
}
authzURLs = append(authzURLs, authz.URI)
// No point in accepting challenges if the authorization status
// is in a final state.
switch authz.Status {
case acme.StatusValid:
return nil // already authorized
case acme.StatusInvalid:
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
}
// Pick the next preferred challenge.
var chal *acme.Challenge
for chal == nil && nextTyp < len(challengeTypes) {
chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges)
nextTyp++
}
if chal == nil {
errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
for chal, err := range errs {
errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
}
return errors.New(errorMsg)
}
cleanup, err := m.fulfill(ctx, client, chal, domain)
if err != nil {
errs[chal] = err
continue
}
defer cleanup()
if _, err := client.Accept(ctx, chal); err != nil {
errs[chal] = err
continue
}
// A challenge is fulfilled and accepted: wait for the CA to validate.
if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
errs[chal] = err
continue
}
return nil
}
}
// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
// using each applicable ACME challenge type.
func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) {
@@ -899,7 +966,7 @@ func httpTokenCacheKey(tokenPath string) string {
return path.Base(tokenPath) + "+http-01"
}
// startRenew starts a cert renewal timer loop, one per domain.
// renew starts a cert renewal timer loop, one per domain.
//
// The loop is scheduled in two cases:
// - a cert was fetched from cache for the first time (wasn't in m.state)
@@ -907,7 +974,7 @@ func httpTokenCacheKey(tokenPath string) string {
//
// The key argument is a certificate private key.
// The exp argument is the cert expiration time (NotAfter).
func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) {
func (m *Manager) renew(ck certKey, key crypto.Signer, exp time.Time) {
m.renewalMu.Lock()
defer m.renewalMu.Unlock()
if m.renewal[ck] != nil {
@@ -1001,7 +1068,7 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
if m.Email != "" {
contact = []string{"mailto:" + m.Email}
}
a := &acme.Account{Contact: contact, ExternalAccountBinding: m.ExternalAccountBinding}
a := &acme.Account{Contact: contact}
_, err := client.Register(ctx, a, m.Prompt)
if err == nil || isAccountAlreadyExist(err) {
m.client = client
@@ -1133,10 +1200,6 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
if err := leaf.VerifyHostname(ck.domain); err != nil {
return nil, err
}
// renew certificates revoked by Let's Encrypt in January 2022
if isRevokedLetsEncrypt(leaf) {
return nil, errors.New("acme/autocert: certificate was probably revoked by Let's Encrypt")
}
// ensure the leaf corresponds to the private key and matches the certKey type
switch pub := leaf.PublicKey.(type) {
case *rsa.PublicKey:
@@ -1167,18 +1230,6 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
return leaf, nil
}
// https://community.letsencrypt.org/t/2022-01-25-issue-with-tls-alpn-01-validation-method/170450
var letsEncryptFixDeployTime = time.Date(2022, time.January, 26, 00, 48, 0, 0, time.UTC)
// isRevokedLetsEncrypt returns whether the certificate is likely to be part of
// a batch of certificates revoked by Let's Encrypt in January 2022. This check
// can be safely removed from May 2022.
func isRevokedLetsEncrypt(cert *x509.Certificate) bool {
O := cert.Issuer.Organization
return len(O) == 1 && O[0] == "Let's Encrypt" &&
cert.NotBefore.Before(letsEncryptFixDeployTime)
}
type lockedMathRand struct {
sync.Mutex
rnd *mathrand.Rand
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -7,6 +7,7 @@ package autocert
import (
"context"
"errors"
"io/ioutil"
"os"
"path/filepath"
)
@@ -40,14 +41,14 @@ type DirCache string
// Get reads a certificate data from the specified file name.
func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
name = filepath.Join(string(d), filepath.Clean("/"+name))
name = filepath.Join(string(d), name)
var (
data []byte
err error
done = make(chan struct{})
)
go func() {
data, err = os.ReadFile(name)
data, err = ioutil.ReadFile(name)
close(done)
}()
select {
@@ -81,7 +82,7 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
case <-ctx.Done():
// Don't overwrite the file if the context was canceled.
default:
newName := filepath.Join(string(d), filepath.Clean("/"+name))
newName := filepath.Join(string(d), name)
err = os.Rename(tmp, newName)
}
}()
@@ -95,7 +96,7 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
// Delete removes the specified file name.
func (d DirCache) Delete(ctx context.Context, name string) error {
name = filepath.Join(string(d), filepath.Clean("/"+name))
name = filepath.Join(string(d), name)
var (
err error
done = make(chan struct{})
@@ -118,7 +119,7 @@ func (d DirCache) Delete(ctx context.Context, name string) error {
// writeTempFile writes b to a temporary file, closes the file and returns its path.
func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) {
// TempFile uses 0600 permissions
f, err := os.CreateTemp(string(d), prefix)
f, err := ioutil.TempFile(string(d), prefix)
if err != nil {
return "", err
}
+2 -1
View File
@@ -6,6 +6,7 @@ package autocert
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -16,7 +17,7 @@ import (
var _ Cache = DirCache("/")
func TestDirCache(t *testing.T) {
dir, err := os.MkdirTemp("", "autocert")
dir, err := ioutil.TempDir("", "autocert")
if err != nil {
t.Fatal(err)
}
-1
View File
@@ -24,7 +24,6 @@ func ExampleManager() {
m := &autocert.Manager{
Cache: autocert.DirCache("secret-dir"),
Prompt: autocert.AcceptTOS,
Email: "example@example.org",
HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"),
}
s := &http.Server{
+185 -429
View File
@@ -8,30 +8,27 @@
package acmetest
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"log"
"math/big"
"net"
"net/http"
"net/http/httptest"
"path"
"sort"
"strconv"
"strings"
"sync"
"testing"
"time"
"golang.org/x/crypto/acme"
@@ -39,65 +36,56 @@ import (
// CAServer is a simple test server which implements ACME spec bits needed for testing.
type CAServer struct {
URL string // server URL after it has been started
Roots *x509.CertPool // CA root certificates; initialized in NewCAServer
rootKey crypto.Signer
rootCert []byte // DER encoding
rootTemplate *x509.Certificate
t *testing.T
server *httptest.Server
issuer pkix.Name
challengeTypes []string
url string
roots *x509.CertPool
eabRequired bool
server *httptest.Server
challengeTypes []string // supported challenge types
domainsWhitelist []string // only these domains are valid for issuing, unless empty
mu sync.Mutex
certCount int // number of issued certs
acctRegistered bool // set once an account has been registered
domainAddr map[string]string // domain name to addr:port resolution
domainGetCert map[string]getCertificateFunc // domain name to GetCertificate function
domainHandler map[string]http.Handler // domain name to Handle function
validAuthz map[string]*authorization // valid authz, keyed by domain name
authorizations []*authorization // all authz, index is used as ID
orders []*order // index is used as order ID
errors []error // encountered client errors
certCount int // number of issued certs
domainAddr map[string]string // domain name to addr:port resolution
authorizations map[string]*authorization // keyed by domain name
orders []*order // index is used as order ID
errors []error // encountered client errors
}
type getCertificateFunc func(hello *tls.ClientHelloInfo) (*tls.Certificate, error)
// NewCAServer creates a new ACME test server. The returned CAServer issues
// certs signed with the CA roots available in the Roots field.
func NewCAServer(t *testing.T) *CAServer {
ca := &CAServer{t: t,
challengeTypes: []string{"fake-01", "tls-alpn-01", "http-01"},
domainAddr: make(map[string]string),
domainGetCert: make(map[string]getCertificateFunc),
domainHandler: make(map[string]http.Handler),
validAuthz: make(map[string]*authorization),
// NewCAServer creates a new ACME test server and starts serving requests.
// The returned CAServer issues certs signed with the CA roots
// available in the Roots field.
//
// The challengeTypes argument defines the supported ACME challenge types
// sent to a client in a response for a domain authorization.
// If domainsWhitelist is non-empty, the certs will be issued only for the specified
// list of domains. Otherwise, any domain name is allowed.
func NewCAServer(challengeTypes []string, domainsWhitelist []string) *CAServer {
var whitelist []string
for _, name := range domainsWhitelist {
whitelist = append(whitelist, name)
}
sort.Strings(whitelist)
ca := &CAServer{
challengeTypes: challengeTypes,
domainsWhitelist: whitelist,
domainAddr: make(map[string]string),
authorizations: make(map[string]*authorization),
}
ca.server = httptest.NewUnstartedServer(http.HandlerFunc(ca.handle))
r, err := rand.Int(rand.Reader, big.NewInt(1000000))
if err != nil {
panic(fmt.Sprintf("rand.Int: %v", err))
}
ca.issuer = pkix.Name{
Organization: []string{"Test Acme Co"},
CommonName: "Root CA " + r.String(),
}
return ca
}
func (ca *CAServer) generateRoot() {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(fmt.Sprintf("ecdsa.GenerateKey: %v", err))
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: ca.issuer,
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Test Acme Co"},
CommonName: "Root CA",
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageCertSign,
@@ -112,96 +100,40 @@ func (ca *CAServer) generateRoot() {
if err != nil {
panic(fmt.Sprintf("x509.ParseCertificate: %v", err))
}
ca.roots = x509.NewCertPool()
ca.roots.AddCert(cert)
ca.Roots = x509.NewCertPool()
ca.Roots.AddCert(cert)
ca.rootKey = key
ca.rootCert = der
ca.rootTemplate = tmpl
}
// IssuerName sets the name of the issuing CA.
func (ca *CAServer) IssuerName(name pkix.Name) *CAServer {
if ca.url != "" {
panic("IssuerName must be called before Start")
}
ca.issuer = name
ca.server = httptest.NewServer(http.HandlerFunc(ca.handle))
ca.URL = ca.server.URL
return ca
}
// ChallengeTypes sets the supported challenge types.
func (ca *CAServer) ChallengeTypes(types ...string) *CAServer {
if ca.url != "" {
panic("ChallengeTypes must be called before Start")
}
ca.challengeTypes = types
return ca
}
// URL returns the server address, after Start has been called.
func (ca *CAServer) URL() string {
if ca.url == "" {
panic("URL called before Start")
}
return ca.url
}
// Roots returns a pool cointaining the CA root.
func (ca *CAServer) Roots() *x509.CertPool {
if ca.url == "" {
panic("Roots called before Start")
}
return ca.roots
}
// ExternalAccountRequired makes an EAB JWS required for account registration.
func (ca *CAServer) ExternalAccountRequired() *CAServer {
if ca.url != "" {
panic("ExternalAccountRequired must be called before Start")
}
ca.eabRequired = true
return ca
}
// Start starts serving requests. The server address becomes available in the
// URL field.
func (ca *CAServer) Start() *CAServer {
if ca.url == "" {
ca.generateRoot()
ca.server.Start()
ca.t.Cleanup(ca.server.Close)
ca.url = ca.server.URL
}
return ca
// Close shuts down the server and blocks until all outstanding
// requests on this server have completed.
func (ca *CAServer) Close() {
ca.server.Close()
}
func (ca *CAServer) serverURL(format string, arg ...interface{}) string {
return ca.server.URL + fmt.Sprintf(format, arg...)
}
func (ca *CAServer) addr(domain string) (string, bool) {
func (ca *CAServer) addr(domain string) (string, error) {
ca.mu.Lock()
defer ca.mu.Unlock()
addr, ok := ca.domainAddr[domain]
return addr, ok
}
func (ca *CAServer) getCert(domain string) (getCertificateFunc, bool) {
ca.mu.Lock()
defer ca.mu.Unlock()
f, ok := ca.domainGetCert[domain]
return f, ok
}
func (ca *CAServer) getHandler(domain string) (http.Handler, bool) {
ca.mu.Lock()
defer ca.mu.Unlock()
h, ok := ca.domainHandler[domain]
return h, ok
if !ok {
return "", fmt.Errorf("CAServer: no addr resolution for %q", domain)
}
return addr, nil
}
func (ca *CAServer) httpErrorf(w http.ResponseWriter, code int, format string, a ...interface{}) {
s := fmt.Sprintf(format, a...)
ca.t.Errorf(format, a...)
log.Println(s)
http.Error(w, s, code)
}
@@ -213,33 +145,11 @@ func (ca *CAServer) Resolve(domain, addr string) {
ca.domainAddr[domain] = addr
}
// ResolveGetCertificate redirects TLS connections for domain to f when
// validating challenges for the domain authorization.
func (ca *CAServer) ResolveGetCertificate(domain string, f getCertificateFunc) {
ca.mu.Lock()
defer ca.mu.Unlock()
ca.domainGetCert[domain] = f
}
// ResolveHandler redirects HTTP requests for domain to f when
// validating challenges for the domain authorization.
func (ca *CAServer) ResolveHandler(domain string, h http.Handler) {
ca.mu.Lock()
defer ca.mu.Unlock()
ca.domainHandler[domain] = h
}
type discovery struct {
NewNonce string `json:"newNonce"`
NewAccount string `json:"newAccount"`
NewOrder string `json:"newOrder"`
NewAuthz string `json:"newAuthz"`
Meta discoveryMeta `json:"meta,omitempty"`
}
type discoveryMeta struct {
ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"`
NewNonce string `json:"newNonce"`
NewReg string `json:"newAccount"`
NewOrder string `json:"newOrder"`
NewAuthz string `json:"newAuthz"`
}
type challenge struct {
@@ -253,7 +163,6 @@ type authorization struct {
Challenges []challenge `json:"challenges"`
domain string
id int
}
type order struct {
@@ -266,7 +175,7 @@ type order struct {
}
func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
ca.t.Logf("%s %s", r.Method, r.URL)
log.Printf("%s %s", r.Method, r.URL)
w.Header().Set("Replay-Nonce", "nonce")
// TODO: Verify nonce header for all POST requests.
@@ -277,12 +186,10 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
// Discovery request.
case r.URL.Path == "/":
resp := &discovery{
NewNonce: ca.serverURL("/new-nonce"),
NewAccount: ca.serverURL("/new-account"),
NewOrder: ca.serverURL("/new-order"),
Meta: discoveryMeta{
ExternalAccountRequired: ca.eabRequired,
},
NewNonce: ca.serverURL("/new-nonce"),
NewReg: ca.serverURL("/new-reg"),
NewOrder: ca.serverURL("/new-order"),
NewAuthz: ca.serverURL("/new-authz"),
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
panic(fmt.Sprintf("discovery response: %v", err))
@@ -294,29 +201,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
return
// Client key registration request.
case r.URL.Path == "/new-account":
ca.mu.Lock()
defer ca.mu.Unlock()
if ca.acctRegistered {
ca.httpErrorf(w, http.StatusServiceUnavailable, "multiple accounts are not implemented")
return
}
ca.acctRegistered = true
var req struct {
ExternalAccountBinding json.RawMessage
}
if err := decodePayload(&req, r.Body); err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
return
}
if ca.eabRequired && len(req.ExternalAccountBinding) == 0 {
ca.httpErrorf(w, http.StatusBadRequest, "registration failed: no JWS for EAB")
return
}
case r.URL.Path == "/new-reg":
// TODO: Check the user account key against a ca.accountKeys?
w.Header().Set("Location", ca.serverURL("/accounts/1"))
w.WriteHeader(http.StatusCreated)
@@ -328,7 +213,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
Identifiers []struct{ Value string }
}
if err := decodePayload(&req, r.Body); err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
ca.mu.Lock()
@@ -336,7 +221,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
o := &order{Status: acme.StatusPending}
for _, id := range req.Identifiers {
z := ca.authz(id.Value)
o.AuthzURLs = append(o.AuthzURLs, ca.serverURL("/authz/%d", z.id))
o.AuthzURLs = append(o.AuthzURLs, ca.serverURL("/authz/%s", z.domain))
}
orderID := len(ca.orders)
ca.orders = append(ca.orders, o)
@@ -352,57 +237,56 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
defer ca.mu.Unlock()
o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/orders/"))
if err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
if err := json.NewEncoder(w).Encode(o); err != nil {
panic(err)
}
// Accept challenge requests.
case strings.HasPrefix(r.URL.Path, "/challenge/"):
parts := strings.Split(r.URL.Path, "/")
typ, id := parts[len(parts)-2], parts[len(parts)-1]
// Identifier authorization request.
case r.URL.Path == "/new-authz":
var req struct {
Identifier struct{ Value string }
}
if err := decodePayload(&req, r.Body); err != nil {
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
ca.mu.Lock()
supported := false
for _, suppTyp := range ca.challengeTypes {
if suppTyp == typ {
supported = true
}
defer ca.mu.Unlock()
z := ca.authz(req.Identifier.Value)
w.Header().Set("Location", ca.serverURL("/authz/%s", z.domain))
w.WriteHeader(http.StatusCreated)
if err := json.NewEncoder(w).Encode(z); err != nil {
panic(fmt.Sprintf("new authz response: %v", err))
}
a, err := ca.storedAuthz(id)
// Accept tls-alpn-01 challenge type requests.
case strings.HasPrefix(r.URL.Path, "/challenge/tls-alpn-01/"):
domain := strings.TrimPrefix(r.URL.Path, "/challenge/tls-alpn-01/")
ca.mu.Lock()
_, exist := ca.authorizations[domain]
ca.mu.Unlock()
if !supported {
ca.httpErrorf(w, http.StatusBadRequest, "unsupported challenge: %v", typ)
if !exist {
ca.httpErrorf(w, http.StatusBadRequest, "challenge accept: no authz for %q", domain)
return
}
if err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "challenge accept: %v", err)
return
}
ca.validateChallenge(a, typ)
go ca.validateChallenge("tls-alpn-01", domain)
w.Write([]byte("{}"))
// Get authorization status requests.
case strings.HasPrefix(r.URL.Path, "/authz/"):
var req struct{ Status string }
decodePayload(&req, r.Body)
deactivate := req.Status == "deactivated"
domain := strings.TrimPrefix(r.URL.Path, "/authz/")
ca.mu.Lock()
defer ca.mu.Unlock()
authz, err := ca.storedAuthz(strings.TrimPrefix(r.URL.Path, "/authz/"))
if err != nil {
ca.httpErrorf(w, http.StatusNotFound, "%v", err)
authz, ok := ca.authorizations[domain]
if !ok {
ca.httpErrorf(w, http.StatusNotFound, "no authz for %q", domain)
return
}
if deactivate {
// Note we don't invalidate authorized orders as we should.
authz.Status = "deactivated"
ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
ca.updatePendingOrders()
}
if err := json.NewEncoder(w).Encode(authz); err != nil {
panic(fmt.Sprintf("encoding authz %d: %v", authz.id, err))
panic(fmt.Sprintf("get authz for %q response: %v", domain, err))
}
// Certificate issuance request.
@@ -412,7 +296,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
orderID := strings.TrimPrefix(r.URL.Path, "/new-cert/")
o, err := ca.storedOrder(orderID)
if err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
if o.Status != acme.StatusReady {
@@ -427,7 +311,16 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
csr, err := x509.ParseCertificateRequest(b)
if err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
names := unique(append(csr.DNSNames, csr.Subject.CommonName))
if err := ca.matchWhitelist(names); err != nil {
ca.httpErrorf(w, http.StatusUnauthorized, err.Error())
return
}
if err := ca.authorized(names); err != nil {
ca.httpErrorf(w, http.StatusUnauthorized, err.Error())
return
}
// Issue the certificate.
@@ -449,7 +342,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
defer ca.mu.Unlock()
o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/issued-cert/"))
if err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "%v", err)
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
if o.Status != acme.StatusValid {
@@ -462,6 +355,25 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
}
}
// matchWhitelist reports whether all dnsNames are whitelisted.
// The whitelist is provided in NewCAServer.
func (ca *CAServer) matchWhitelist(dnsNames []string) error {
if len(ca.domainsWhitelist) == 0 {
return nil
}
var nomatch []string
for _, name := range dnsNames {
i := sort.SearchStrings(ca.domainsWhitelist, name)
if i == len(ca.domainsWhitelist) || ca.domainsWhitelist[i] != name {
nomatch = append(nomatch, name)
}
}
if len(nomatch) > 0 {
return fmt.Errorf("matchWhitelist: some domains don't match: %q", nomatch)
}
return nil
}
// storedOrder retrieves a previously created order at index i.
// It requires ca.mu to be locked.
func (ca *CAServer) storedOrder(i string) (*order, error) {
@@ -475,50 +387,46 @@ func (ca *CAServer) storedOrder(i string) (*order, error) {
if idx > len(ca.orders)-1 {
return nil, fmt.Errorf("storedOrder: no such order %d", idx)
}
ca.updatePendingOrders()
return ca.orders[idx], nil
}
// storedAuthz retrieves a previously created authz at index i.
// authz returns an existing authorization for the identifier or creates a new one.
// It requires ca.mu to be locked.
func (ca *CAServer) storedAuthz(i string) (*authorization, error) {
idx, err := strconv.Atoi(i)
if err != nil {
return nil, fmt.Errorf("storedAuthz: %v", err)
}
if idx < 0 {
return nil, fmt.Errorf("storedAuthz: invalid authz index %d", idx)
}
if idx > len(ca.authorizations)-1 {
return nil, fmt.Errorf("storedAuthz: no such authz %d", idx)
}
return ca.authorizations[idx], nil
}
// authz returns an existing valid authorization for the identifier or creates a
// new one. It requires ca.mu to be locked.
func (ca *CAServer) authz(identifier string) *authorization {
authz, ok := ca.validAuthz[identifier]
authz, ok := ca.authorizations[identifier]
if !ok {
authzId := len(ca.authorizations)
authz = &authorization{
id: authzId,
domain: identifier,
Status: acme.StatusPending,
}
for _, typ := range ca.challengeTypes {
authz.Challenges = append(authz.Challenges, challenge{
Type: typ,
URI: ca.serverURL("/challenge/%s/%d", typ, authzId),
Token: challengeToken(authz.domain, typ, authzId),
URI: ca.serverURL("/challenge/%s/%s", typ, authz.domain),
Token: challengeToken(authz.domain, typ),
})
}
ca.authorizations = append(ca.authorizations, authz)
ca.authorizations[authz.domain] = authz
}
return authz
}
// authorized reports whether all authorizations for dnsNames have been satisfied.
// It requires ca.mu to be locked.
func (ca *CAServer) authorized(dnsNames []string) error {
var noauthz []string
for _, name := range dnsNames {
authz, ok := ca.authorizations[name]
if !ok || authz.Status != acme.StatusValid {
noauthz = append(noauthz, name)
}
}
if len(noauthz) > 0 {
return fmt.Errorf("CAServer: no authz for %q", noauthz)
}
return nil
}
// leafCert issues a new certificate.
// It requires ca.mu to be locked.
func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err error) {
@@ -539,231 +447,79 @@ func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err erro
return x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, csr.PublicKey, ca.rootKey)
}
// LeafCert issues a leaf certificate.
func (ca *CAServer) LeafCert(name, keyType string, notBefore, notAfter time.Time) *tls.Certificate {
if ca.url == "" {
panic("LeafCert called before Start")
}
ca.mu.Lock()
defer ca.mu.Unlock()
var pk crypto.Signer
switch keyType {
case "RSA":
var err error
pk, err = rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
ca.t.Fatal(err)
}
case "ECDSA":
var err error
pk, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
ca.t.Fatal(err)
}
default:
panic("LeafCert: unknown key type")
}
ca.certCount++ // next leaf cert serial number
leaf := &x509.Certificate{
SerialNumber: big.NewInt(int64(ca.certCount)),
Subject: pkix.Name{Organization: []string{"Test Acme Co"}},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
DNSNames: []string{name},
BasicConstraintsValid: true,
}
der, err := x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, pk.Public(), ca.rootKey)
if err != nil {
ca.t.Fatal(err)
}
return &tls.Certificate{
Certificate: [][]byte{der},
PrivateKey: pk,
}
}
func (ca *CAServer) validateChallenge(authz *authorization, typ string) {
// TODO: Only tls-alpn-01 is currently supported: implement http-01 and dns-01.
func (ca *CAServer) validateChallenge(typ, identifier string) {
var err error
switch typ {
case "tls-alpn-01":
err = ca.verifyALPNChallenge(authz)
case "http-01":
err = ca.verifyHTTPChallenge(authz)
err = ca.verifyALPNChallenge(identifier)
default:
panic(fmt.Sprintf("validation of %q is not implemented", typ))
}
ca.mu.Lock()
defer ca.mu.Unlock()
authz := ca.authorizations[identifier]
if err != nil {
authz.Status = "invalid"
} else {
authz.Status = "valid"
ca.validAuthz[authz.domain] = authz
}
ca.t.Logf("validated %q for %q, err: %v", typ, authz.domain, err)
ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
ca.updatePendingOrders()
}
func (ca *CAServer) updatePendingOrders() {
log.Printf("validated %q for %q; authz status is now: %s", typ, identifier, authz.Status)
// Update all pending orders.
// An order becomes "ready" if all authorizations are "valid".
// An order becomes "invalid" if any authorization is "invalid".
// Status changes: https://tools.ietf.org/html/rfc8555#section-7.1.6
OrdersLoop:
for i, o := range ca.orders {
if o.Status != acme.StatusPending {
continue
}
countValid, countInvalid := ca.validateAuthzURLs(o.AuthzURLs, i)
if countInvalid > 0 {
o.Status = acme.StatusInvalid
ca.t.Logf("order %d is now invalid", i)
continue
var countValid int
for _, zurl := range o.AuthzURLs {
z, ok := ca.authorizations[path.Base(zurl)]
if !ok {
log.Printf("no authz %q for order %d", zurl, i)
continue OrdersLoop
}
if z.Status == acme.StatusInvalid {
o.Status = acme.StatusInvalid
log.Printf("order %d is now invalid", i)
continue OrdersLoop
}
if z.Status == acme.StatusValid {
countValid++
}
}
if countValid == len(o.AuthzURLs) {
o.Status = acme.StatusReady
o.FinalizeURL = ca.serverURL("/new-cert/%d", i)
ca.t.Logf("order %d is now ready", i)
log.Printf("order %d is now ready", i)
}
}
}
func (ca *CAServer) validateAuthzURLs(urls []string, orderNum int) (countValid, countInvalid int) {
for _, zurl := range urls {
z, err := ca.storedAuthz(path.Base(zurl))
if err != nil {
ca.t.Logf("no authz %q for order %d", zurl, orderNum)
continue
}
if z.Status == acme.StatusInvalid {
countInvalid++
}
if z.Status == acme.StatusValid {
countValid++
}
}
return countValid, countInvalid
}
func (ca *CAServer) verifyALPNChallenge(a *authorization) error {
func (ca *CAServer) verifyALPNChallenge(domain string) error {
const acmeALPNProto = "acme-tls/1"
addr, haveAddr := ca.addr(a.domain)
getCert, haveGetCert := ca.getCert(a.domain)
if !haveAddr && !haveGetCert {
return fmt.Errorf("no resolution information for %q", a.domain)
addr, err := ca.addr(domain)
if err != nil {
return err
}
if haveAddr && haveGetCert {
return fmt.Errorf("overlapping resolution information for %q", a.domain)
conn, err := tls.Dial("tcp", addr, &tls.Config{
ServerName: domain,
InsecureSkipVerify: true,
NextProtos: []string{acmeALPNProto},
})
if err != nil {
return err
}
var crt *x509.Certificate
switch {
case haveAddr:
conn, err := tls.Dial("tcp", addr, &tls.Config{
ServerName: a.domain,
InsecureSkipVerify: true,
NextProtos: []string{acmeALPNProto},
MinVersion: tls.VersionTLS12,
})
if err != nil {
return err
}
if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
}
if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
}
crt = conn.ConnectionState().PeerCertificates[0]
case haveGetCert:
hello := &tls.ClientHelloInfo{
ServerName: a.domain,
// TODO: support selecting ECDSA.
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
SupportedProtos: []string{acme.ALPNProto},
SupportedVersions: []uint16{tls.VersionTLS12},
}
c, err := getCert(hello)
if err != nil {
return err
}
crt, err = x509.ParseCertificate(c.Certificate[0])
if err != nil {
return err
}
if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
}
if err := crt.VerifyHostname(a.domain); err != nil {
return fmt.Errorf("verifyALPNChallenge: VerifyHostname: %v", err)
}
// See RFC 8737, Section 6.1.
oid := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
for _, x := range crt.Extensions {
if x.Id.Equal(oid) {
// TODO: check the token.
return nil
}
}
return fmt.Errorf("verifyTokenCert: no id-pe-acmeIdentifier extension found")
}
func (ca *CAServer) verifyHTTPChallenge(a *authorization) error {
addr, haveAddr := ca.addr(a.domain)
handler, haveHandler := ca.getHandler(a.domain)
if !haveAddr && !haveHandler {
return fmt.Errorf("no resolution information for %q", a.domain)
}
if haveAddr && haveHandler {
return fmt.Errorf("overlapping resolution information for %q", a.domain)
}
token := challengeToken(a.domain, "http-01", a.id)
path := "/.well-known/acme-challenge/" + token
var body string
switch {
case haveAddr:
t := &http.Transport{
DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, network, addr)
},
}
req, err := http.NewRequest("GET", "http://"+a.domain+path, nil)
if err != nil {
return err
}
res, err := t.RoundTrip(req)
if err != nil {
return err
}
if res.StatusCode != http.StatusOK {
return fmt.Errorf("http token: w.Code = %d; want %d", res.StatusCode, http.StatusOK)
}
b, err := io.ReadAll(res.Body)
if err != nil {
return err
}
body = string(b)
case haveHandler:
r := httptest.NewRequest("GET", path, nil)
r.Host = a.domain
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
return fmt.Errorf("http token: w.Code = %d; want %d", w.Code, http.StatusOK)
}
body = w.Body.String()
}
if !strings.HasPrefix(body, token) {
return fmt.Errorf("http token value = %q; want 'token-http-01.' prefix", body)
if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
}
// TODO: verify conn.ConnectionState().PeerCertificates[0]
return nil
}
@@ -779,8 +535,8 @@ func decodePayload(v interface{}, r io.Reader) error {
return json.Unmarshal(payload, v)
}
func challengeToken(domain, challType string, authzID int) string {
return fmt.Sprintf("token-%s-%s-%d", domain, challType, authzID)
func challengeToken(domain, challType string) string {
return fmt.Sprintf("token-%s-%s", domain, challType)
}
func unique(a []string) []string {
+1 -1
View File
@@ -20,7 +20,7 @@ import (
//
// It enables one-line HTTPS servers:
//
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
//
// NewListener is a convenience function for a common configuration.
// More complex or custom configurations can use the autocert.Manager
+10 -25
View File
@@ -21,9 +21,8 @@ type domainRenewal struct {
ck certKey
key crypto.Signer
timerMu sync.Mutex
timer *time.Timer
timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running
timerMu sync.Mutex
timer *time.Timer
}
// start starts a cert renewal timer at the time
@@ -39,28 +38,16 @@ func (dr *domainRenewal) start(exp time.Time) {
dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
}
// stop stops the cert renewal timer and waits for any in-flight calls to renew
// to complete. If the timer is already stopped, calling stop is a noop.
// stop stops the cert renewal timer.
// If the timer is already stopped, calling stop is a noop.
func (dr *domainRenewal) stop() {
dr.timerMu.Lock()
defer dr.timerMu.Unlock()
for {
if dr.timer == nil {
return
}
if dr.timer.Stop() {
dr.timer = nil
return
} else {
// dr.timer fired, and we acquired dr.timerMu before the renew callback did.
// (We know this because otherwise the renew callback would have reset dr.timer!)
timerClose := make(chan struct{})
dr.timerClose = timerClose
dr.timerMu.Unlock()
<-timerClose
dr.timerMu.Lock()
}
if dr.timer == nil {
return
}
dr.timer.Stop()
dr.timer = nil
}
// renew is called periodically by a timer.
@@ -68,9 +55,7 @@ func (dr *domainRenewal) stop() {
func (dr *domainRenewal) renew() {
dr.timerMu.Lock()
defer dr.timerMu.Unlock()
if dr.timerClose != nil {
close(dr.timerClose)
dr.timer, dr.timerClose = nil, nil
if dr.timer == nil {
return
}
@@ -82,8 +67,8 @@ func (dr *domainRenewal) renew() {
next = renewJitter / 2
next += time.Duration(pseudoRand.int63n(int64(next)))
}
testDidRenewLoop(next, err)
dr.timer = time.AfterFunc(next, dr.renew)
testDidRenewLoop(next, err)
}
// updateState locks and replaces the relevant Manager.state item with the given
+156 -93
View File
@@ -6,13 +6,19 @@ package autocert
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert/internal/acmetest"
)
func TestRenewalNext(t *testing.T) {
@@ -42,47 +48,101 @@ func TestRenewalNext(t *testing.T) {
}
func TestRenewFromCache(t *testing.T) {
man := testManager(t)
man.RenewBefore = 24 * time.Hour
// ACME CA server stub
var ca *httptest.Server
ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Replay-Nonce", "nonce")
if r.Method == "HEAD" {
// a nonce request
return
}
ca := acmetest.NewCAServer(t).Start()
ca.ResolveGetCertificate(exampleDomain, man.GetCertificate)
switch r.URL.Path {
// discovery
case "/":
if err := discoTmpl.Execute(w, ca.URL); err != nil {
t.Fatalf("discoTmpl: %v", err)
}
// client key registration
case "/new-reg":
w.Write([]byte("{}"))
// domain authorization
case "/new-authz":
w.Header().Set("Location", ca.URL+"/authz/1")
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"status": "valid"}`))
// authorization status request done by Manager's revokePendingAuthz.
case "/authz/1":
w.Write([]byte(`{"status": "valid"}`))
// cert request
case "/new-cert":
var req struct {
CSR string `json:"csr"`
}
decodePayload(&req, r.Body)
b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
csr, err := x509.ParseCertificateRequest(b)
if err != nil {
t.Fatalf("new-cert: CSR: %v", err)
}
der, err := dummyCert(csr.PublicKey, exampleDomain)
if err != nil {
t.Fatalf("new-cert: dummyCert: %v", err)
}
chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
w.Header().Set("Link", chainUp)
w.WriteHeader(http.StatusCreated)
w.Write(der)
// CA chain cert
case "/ca-cert":
der, err := dummyCert(nil, "ca")
if err != nil {
t.Fatalf("ca-cert: dummyCert: %v", err)
}
w.Write(der)
default:
t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
}
}))
defer ca.Close()
man.Client = &acme.Client{
DirectoryURL: ca.URL(),
man := &Manager{
Prompt: AcceptTOS,
Cache: newMemCache(t),
RenewBefore: 24 * time.Hour,
Client: &acme.Client{
DirectoryURL: ca.URL,
},
}
defer man.stopRenew()
// cache an almost expired cert
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
now := time.Now()
c := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
if err != nil {
t.Fatal(err)
}
tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
if err := man.cachePut(context.Background(), exampleCertKey, tlscert); err != nil {
t.Fatal(err)
}
// verify the renewal happened
// veriy the renewal happened
defer func() {
// Stop the timers that read and execute testDidRenewLoop before restoring it.
// Otherwise the timer callback may race with the deferred write.
man.stopRenew()
testDidRenewLoop = func(next time.Duration, err error) {}
}()
renewed := make(chan bool, 1)
done := make(chan struct{})
testDidRenewLoop = func(next time.Duration, err error) {
defer func() {
select {
case renewed <- true:
default:
// The renewal timer uses a random backoff. If the first renewal fails for
// some reason, we could end up with multiple calls here before the test
// stops the timer.
}
}()
defer close(done)
if err != nil {
t.Errorf("testDidRenewLoop: %v", err)
}
// Next should be about 90 days:
// CaServer creates 90days expiry + account for man.RenewBefore.
// dummyCert creates 90days expiry + account for man.RenewBefore.
// Previous expiration was within 1 min.
future := 88 * 24 * time.Hour
if next < future {
@@ -93,8 +153,7 @@ func TestRenewFromCache(t *testing.T) {
after := time.Now().Add(future)
tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
if err != nil {
t.Errorf("man.cacheGet: %v", err)
return
t.Fatalf("man.cacheGet: %v", err)
}
if !tlscert.Leaf.NotAfter.After(after) {
t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
@@ -105,13 +164,11 @@ func TestRenewFromCache(t *testing.T) {
defer man.stateMu.Unlock()
s := man.state[exampleCertKey]
if s == nil {
t.Errorf("m.state[%q] is nil", exampleCertKey)
return
t.Fatalf("m.state[%q] is nil", exampleCertKey)
}
tlscert, err = s.tlscert()
if err != nil {
t.Errorf("s.tlscert: %v", err)
return
t.Fatalf("s.tlscert: %v", err)
}
if !tlscert.Leaf.NotAfter.After(after) {
t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
@@ -123,34 +180,55 @@ func TestRenewFromCache(t *testing.T) {
if _, err := man.GetCertificate(hello); err != nil {
t.Fatal(err)
}
<-renewed
// wait for renew loop
select {
case <-time.After(10 * time.Second):
t.Fatal("renew took too long to occur")
case <-done:
}
}
func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
ca := acmetest.NewCAServer(t).Start()
man := testManager(t)
man.RenewBefore = 24 * time.Hour
man.Client = &acme.Client{
DirectoryURL: "invalid",
man := &Manager{
Prompt: AcceptTOS,
Cache: newMemCache(t),
RenewBefore: 24 * time.Hour,
Client: &acme.Client{
DirectoryURL: "invalid",
},
}
defer man.stopRenew()
// cache a recently renewed cert with a different private key
now := time.Now()
newCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Hour*24*90))
if err := man.cachePut(context.Background(), exampleCertKey, newCert); err != nil {
newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
newLeaf, err := validCert(exampleCertKey, newCert.Certificate, newCert.PrivateKey.(crypto.Signer), now)
now := time.Now()
newCert, err := dateDummyCert(newKey.Public(), now.Add(-2*time.Hour), now.Add(time.Hour*24*90), exampleDomain)
if err != nil {
t.Fatal(err)
}
newLeaf, err := validCert(exampleCertKey, [][]byte{newCert}, newKey, now)
if err != nil {
t.Fatal(err)
}
newTLSCert := &tls.Certificate{PrivateKey: newKey, Certificate: [][]byte{newCert}, Leaf: newLeaf}
if err := man.cachePut(context.Background(), exampleCertKey, newTLSCert); err != nil {
t.Fatal(err)
}
// set internal state to an almost expired cert
oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
oldCert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
if err != nil {
t.Fatal(err)
}
oldLeaf, err := validCert(exampleCertKey, [][]byte{oldCert}, key, now)
if err != nil {
t.Fatal(err)
}
@@ -159,32 +237,20 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
man.state = make(map[certKey]*certState)
}
s := &certState{
key: oldCert.PrivateKey.(crypto.Signer),
cert: oldCert.Certificate,
key: key,
cert: [][]byte{oldCert},
leaf: oldLeaf,
}
man.state[exampleCertKey] = s
man.stateMu.Unlock()
// verify the renewal accepted the newer cached cert
// veriy the renewal accepted the newer cached cert
defer func() {
// Stop the timers that read and execute testDidRenewLoop before restoring it.
// Otherwise the timer callback may race with the deferred write.
man.stopRenew()
testDidRenewLoop = func(next time.Duration, err error) {}
}()
renewed := make(chan bool, 1)
done := make(chan struct{})
testDidRenewLoop = func(next time.Duration, err error) {
defer func() {
select {
case renewed <- true:
default:
// The renewal timer uses a random backoff. If the first renewal fails for
// some reason, we could end up with multiple calls here before the test
// stops the timer.
}
}()
defer close(done)
if err != nil {
t.Errorf("testDidRenewLoop: %v", err)
}
@@ -198,8 +264,7 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
// ensure the cached cert was not modified
tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
if err != nil {
t.Errorf("man.cacheGet: %v", err)
return
t.Fatalf("man.cacheGet: %v", err)
}
if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
@@ -210,22 +275,30 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
defer man.stateMu.Unlock()
s := man.state[exampleCertKey]
if s == nil {
t.Errorf("m.state[%q] is nil", exampleCertKey)
return
t.Fatalf("m.state[%q] is nil", exampleCertKey)
}
stateKey := s.key.Public().(*ecdsa.PublicKey)
if !stateKey.Equal(newLeaf.PublicKey) {
t.Error("state key was not updated from cache")
return
if stateKey.X.Cmp(newKey.X) != 0 || stateKey.Y.Cmp(newKey.Y) != 0 {
t.Fatalf("state key was not updated from cache x: %v y: %v; want x: %v y: %v", stateKey.X, stateKey.Y, newKey.X, newKey.Y)
}
tlscert, err = s.tlscert()
if err != nil {
t.Errorf("s.tlscert: %v", err)
return
t.Fatalf("s.tlscert: %v", err)
}
if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
}
// verify the private key is replaced in the renewal state
r := man.renewal[exampleCertKey]
if r == nil {
t.Fatalf("m.renewal[%q] is nil", exampleCertKey)
}
renewalKey := r.key.Public().(*ecdsa.PublicKey)
if renewalKey.X.Cmp(newKey.X) != 0 || renewalKey.Y.Cmp(newKey.Y) != 0 {
t.Fatalf("renewal private key was not updated from cache x: %v y: %v; want x: %v y: %v", renewalKey.X, renewalKey.Y, newKey.X, newKey.Y)
}
}
// assert the expiring cert is returned from state
@@ -239,31 +312,21 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
}
// trigger renew
man.startRenew(exampleCertKey, s.key, s.leaf.NotAfter)
<-renewed
func() {
man.renewalMu.Lock()
defer man.renewalMu.Unlock()
go man.renew(exampleCertKey, s.key, s.leaf.NotAfter)
// verify the private key is replaced in the renewal state
r := man.renewal[exampleCertKey]
if r == nil {
t.Errorf("m.renewal[%q] is nil", exampleCertKey)
return
// wait for renew loop
select {
case <-time.After(10 * time.Second):
t.Fatal("renew took too long to occur")
case <-done:
// assert the new cert is returned from state after renew
hello := clientHelloInfo(exampleDomain, algECDSA)
tlscert, err := man.GetCertificate(hello)
if err != nil {
t.Fatal(err)
}
renewalKey := r.key.Public().(*ecdsa.PublicKey)
if !renewalKey.Equal(newLeaf.PublicKey) {
t.Error("renewal private key was not updated from cache")
if !newTLSCert.Leaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newTLSCert.Leaf.NotAfter)
}
}()
// assert the new cert is returned from state after renew
hello = clientHelloInfo(exampleDomain, algECDSA)
tlscert, err = man.GetCertificate(hello)
if err != nil {
t.Fatal(err)
}
if !newLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
}
}
+4 -27
View File
@@ -10,12 +10,10 @@ import (
"crypto"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"runtime/debug"
"strconv"
"strings"
"time"
@@ -157,7 +155,7 @@ func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Respons
}
}
// postAsGet is POST-as-GET, a replacement for GET in RFC 8555
// postAsGet is POST-as-GET, a replacement for GET in RFC8555
// as described in https://tools.ietf.org/html/rfc8555#section-6.3.
// It makes a POST request in KID form with zero JWS payload.
// See nopayload doc comments in jws.go.
@@ -217,9 +215,6 @@ func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body i
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
kid := noKeyID
if key == nil {
if c.Key == nil {
return nil, nil, errors.New("acme: Client.Key must be populated to make POST requests")
}
key = c.Key
kid = c.accountKID(ctx)
}
@@ -272,27 +267,9 @@ func (c *Client) httpClient() *http.Client {
}
// packageVersion is the version of the module that contains this package, for
// sending as part of the User-Agent header.
// sending as part of the User-Agent header. It's set in version_go112.go.
var packageVersion string
func init() {
// Set packageVersion if the binary was built in modules mode and x/crypto
// was not replaced with a different module.
info, ok := debug.ReadBuildInfo()
if !ok {
return
}
for _, m := range info.Deps {
if m.Path != "golang.org/x/crypto" {
continue
}
if m.Replace == nil {
packageVersion = m.Version
}
break
}
}
// userAgent returns the User-Agent header value. It includes the package name,
// the module version (if available), and the c.UserAgent value (if set).
func (c *Client) userAgent() string {
@@ -329,7 +306,7 @@ func isRetriable(code int) bool {
func responseError(resp *http.Response) error {
// don't care if ReadAll returns an error:
// json.Unmarshal will fail in that case anyway
b, _ := io.ReadAll(resp.Body)
b, _ := ioutil.ReadAll(resp.Body)
e := &wireError{Status: resp.StatusCode}
if err := json.Unmarshal(b, e); err != nil {
// this is not a regular error response:
+5 -20
View File
@@ -7,7 +7,7 @@ package acme
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
@@ -54,7 +54,7 @@ func TestErrorResponse(t *testing.T) {
res := &http.Response{
StatusCode: 400,
Status: "400 Bad Request",
Body: io.NopCloser(strings.NewReader(s)),
Body: ioutil.NopCloser(strings.NewReader(s)),
Header: http.Header{"X-Foo": {"bar"}},
}
err := responseError(res)
@@ -115,8 +115,8 @@ func TestPostWithRetries(t *testing.T) {
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 1: %v", err)
}
if count != 3 {
t.Errorf("total requests count: %d; want 3", count)
if count != 4 {
t.Errorf("total requests count: %d; want 4", count)
}
}
@@ -224,7 +224,7 @@ func TestUserAgent(t *testing.T) {
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"newOrder": "sure"}`))
w.Write([]byte(`{}`))
}))
defer ts.Close()
@@ -238,18 +238,3 @@ func TestUserAgent(t *testing.T) {
}
}
}
func TestAccountKidLoop(t *testing.T) {
// if Client.postNoRetry is called with a nil key argument
// then Client.Key must be set, otherwise we fall into an
// infinite loop (which also causes a deadlock).
client := &Client{dir: &Directory{OrderURL: ":)"}}
_, _, err := client.postNoRetry(context.Background(), nil, "", nil)
if err == nil {
t.Fatal("Client.postNoRetry didn't fail with a nil key")
}
expected := "acme: Client.Key must be populated to make POST requests"
if err.Error() != expected {
t.Fatalf("Unexpected error returned: wanted %q, got %q", expected, err.Error())
}
}
+54 -7
View File
@@ -11,12 +11,12 @@
//
// A usage example:
//
// go run prober.go \
// -d https://acme-staging-v02.api.letsencrypt.org/directory \
// -f order \
// -t http-01 \
// -a :8080 \
// -domain some.example.org
// go run prober.go \
// -d https://acme-staging-v02.api.letsencrypt.org/directory \
// -f order \
// -t http-01 \
// -a :8080 \
// -domain some.example.org
//
// The above assumes a TCP tunnel from some.example.org:80 to 0.0.0.0:8080
// in order for the test to be able to fulfill http-01 challenge.
@@ -50,13 +50,14 @@ import (
var (
// ACME CA directory URL.
// Let's Encrypt v1 prod: https://acme-v01.api.letsencrypt.org/directory
// Let's Encrypt v2 prod: https://acme-v02.api.letsencrypt.org/directory
// Let's Encrypt v2 staging: https://acme-staging-v02.api.letsencrypt.org/directory
// See the following for more CAs implementing ACME protocol:
// https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment#CAs_&_PKIs_that_offer_ACME_certificates
directory = flag.String("d", "", "ACME directory URL.")
reginfo = flag.String("r", "", "ACME account registration info.")
flow = flag.String("f", "", `Flow to run: "order" or "preauthz" (RFC8555).`)
flow = flag.String("f", "", "Flow to run: order, preauthz (RFC8555) or preauthz02 (draft-02).")
chaltyp = flag.String("t", "", "Challenge type: tls-alpn-01, http-01 or dns-01.")
addr = flag.String("a", "", "Local server address for tls-alpn-01 and http-01.")
dnsscript = flag.String("s", "", "Script to run for provisioning dns-01 challenges.")
@@ -126,6 +127,8 @@ When running with dns-01 challenge type, use -s argument instead of -a.
p.runOrder(ctx, identifiers)
case "preauthz":
p.runPreauthz(ctx, identifiers)
case "preauthz02":
p.runPreauthzLegacy(ctx, identifiers)
default:
log.Fatalf("unknown flow: %q", *flow)
}
@@ -273,6 +276,50 @@ func (p *prober) runPreauthz(ctx context.Context, identifiers []acme.AuthzID) {
}
}
func (p *prober) runPreauthzLegacy(ctx context.Context, identifiers []acme.AuthzID) {
var zurls []string
for _, id := range identifiers {
z, err := authorize(ctx, p.client, id)
if err != nil {
log.Fatalf("AuthorizeID(%+v): %v", id, err)
}
if z.Status == acme.StatusValid {
log.Printf("authz %s is valid; skipping", z.URI)
continue
}
if err := p.fulfill(ctx, z); err != nil {
log.Fatalf("fulfill(%s): %v", z.URI, err)
}
zurls = append(zurls, z.URI)
log.Printf("authorized for %+v", id)
}
// We should be all set now.
log.Print("all authorizations are done")
csr, certkey := newCSR(identifiers)
der, curl, err := p.client.CreateCert(ctx, csr, 48*time.Hour, true)
if err != nil {
log.Fatalf("CreateCert: %v", err)
}
log.Printf("cert URL: %s", curl)
if err := checkCert(der, identifiers); err != nil {
p.errorf("invalid cert: %v", err)
}
// Deactivate all authorizations we satisfied earlier.
for _, v := range zurls {
if err := p.client.RevokeAuthorization(ctx, v); err != nil {
p.errorf("RevokAuthorization(%q): %v", v, err)
continue
}
}
// Try revoking the issued cert using its private key.
if err := p.client.RevokeCert(ctx, certkey, der[0], acme.CRLReasonCessationOfOperation); err != nil {
p.errorf("RevokeCert: %v", err)
}
}
func (p *prober) fulfill(ctx context.Context, z *acme.Authorization) error {
var chal *acme.Challenge
for i, c := range z.Challenges {
+11 -39
View File
@@ -20,12 +20,12 @@ import (
"math/big"
)
// KeyID is the account key identity provided by a CA during registration.
type KeyID string
// keyID is the account identity provided by a CA during registration.
type keyID string
// noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID.
// See jwsEncodeJSON for details.
const noKeyID = KeyID("")
const noKeyID = keyID("")
// noPayload indicates jwsEncodeJSON will encode zero-length octet string
// in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make
@@ -33,10 +33,6 @@ const noKeyID = KeyID("")
// See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
const noPayload = ""
// noNonce indicates that the nonce should be omitted from the protected header.
// See jwsEncodeJSON for details.
const noNonce = ""
// jsonWebSignature can be easily serialized into a JWS following
// https://tools.ietf.org/html/rfc7515#section-3.2.
type jsonWebSignature struct {
@@ -47,56 +43,32 @@ type jsonWebSignature struct {
// jwsEncodeJSON signs claimset using provided key and a nonce.
// The result is serialized in JSON format containing either kid or jwk
// fields based on the provided KeyID value.
// fields based on the provided keyID value.
//
// The claimset is marshalled using json.Marshal unless it is a string.
// In which case it is inserted directly into the message.
//
// If kid is non-empty, its quoted value is inserted in the protected header
// If kid is non-empty, its quoted value is inserted in the protected head
// as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted
// as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive.
//
// If nonce is non-empty, its quoted value is inserted in the protected header.
//
// See https://tools.ietf.org/html/rfc7515#section-7.
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, url string) ([]byte, error) {
if key == nil {
return nil, errors.New("nil key")
}
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) {
alg, sha := jwsHasher(key.Public())
if alg == "" || !sha.Available() {
return nil, ErrUnsupportedKey
}
headers := struct {
Alg string `json:"alg"`
KID string `json:"kid,omitempty"`
JWK json.RawMessage `json:"jwk,omitempty"`
Nonce string `json:"nonce,omitempty"`
URL string `json:"url"`
}{
Alg: alg,
Nonce: nonce,
URL: url,
}
var phead string
switch kid {
case noKeyID:
jwk, err := jwkEncode(key.Public())
if err != nil {
return nil, err
}
headers.JWK = json.RawMessage(jwk)
phead = fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q,"url":%q}`, alg, jwk, nonce, url)
default:
headers.KID = string(kid)
phead = fmt.Sprintf(`{"alg":%q,"kid":%q,"nonce":%q,"url":%q}`, alg, kid, nonce, url)
}
phJSON, err := json.Marshal(headers)
if err != nil {
return nil, err
}
phead := base64.RawURLEncoding.EncodeToString([]byte(phJSON))
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
var payload string
if val, ok := claimset.(string); ok {
payload = val
} else {
if claimset != noPayload {
cs, err := json.Marshal(claimset)
if err != nil {
return nil, err
+1 -39
View File
@@ -195,46 +195,8 @@ func TestJWSEncodeJSON(t *testing.T) {
}
}
func TestJWSEncodeNoNonce(t *testing.T) {
kid := KeyID("https://example.org/account/1")
claims := "RawString"
const (
// {"alg":"ES256","kid":"https://example.org/account/1","nonce":"nonce","url":"url"}
protected = "eyJhbGciOiJFUzI1NiIsImtpZCI6Imh0dHBzOi8vZXhhbXBsZS5vcmcvYWNjb3VudC8xIiwidXJsIjoidXJsIn0"
// "Raw String"
payload = "RawString"
)
b, err := jwsEncodeJSON(claims, testKeyEC, kid, "", "url")
if err != nil {
t.Fatal(err)
}
var jws struct{ Protected, Payload, Signature string }
if err := json.Unmarshal(b, &jws); err != nil {
t.Fatal(err)
}
if jws.Protected != protected {
t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected)
}
if jws.Payload != payload {
t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload)
}
sig, err := base64.RawURLEncoding.DecodeString(jws.Signature)
if err != nil {
t.Fatalf("jws.Signature: %v", err)
}
r, s := big.NewInt(0), big.NewInt(0)
r.SetBytes(sig[:len(sig)/2])
s.SetBytes(sig[len(sig)/2:])
h := sha256.Sum256([]byte(protected + "." + payload))
if !ecdsa.Verify(testKeyEC.Public().(*ecdsa.PublicKey), h[:], r, s) {
t.Error("invalid signature")
}
}
func TestJWSEncodeKID(t *testing.T) {
kid := KeyID("https://example.org/account/1")
kid := keyID("https://example.org/account/1")
claims := struct{ Msg string }{"Hello JWS"}
// JWS signed with testKeyEC
const (
+4 -68
View File
@@ -13,6 +13,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
)
@@ -23,9 +24,6 @@ import (
//
// It only works with CAs implementing RFC 8555.
func (c *Client) DeactivateReg(ctx context.Context) error {
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
return err
}
url := string(c.accountKID(ctx))
if url == "" {
return ErrNoAccount
@@ -80,7 +78,7 @@ func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tos
}
// Cache Account URL even if we return an error to the caller.
// It is by all means a valid and usable "kid" value for future requests.
c.KID = KeyID(a.URI)
c.kid = keyID(a.URI)
if res.StatusCode == http.StatusOK {
return nil, ErrAccountAlreadyExists
}
@@ -117,7 +115,7 @@ func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error)
return responseAccount(res)
}
// getRegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
// getGegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
// It expects c.Discover to have already been called.
func (c *Client) getRegRFC(ctx context.Context) (*Account, error) {
req := json.RawMessage(`{"onlyReturnExisting": true}`)
@@ -150,42 +148,6 @@ func responseAccount(res *http.Response) (*Account, error) {
}, nil
}
// accountKeyRollover attempts to perform account key rollover.
// On success it will change client.Key to the new key.
func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
dir, err := c.Discover(ctx) // Also required by c.accountKID
if err != nil {
return err
}
kid := c.accountKID(ctx)
if kid == noKeyID {
return ErrNoAccount
}
oldKey, err := jwkEncode(c.Key.Public())
if err != nil {
return err
}
payload := struct {
Account string `json:"account"`
OldKey json.RawMessage `json:"oldKey"`
}{
Account: string(kid),
OldKey: json.RawMessage(oldKey),
}
inner, err := jwsEncodeJSON(payload, newKey, noKeyID, noNonce, dir.KeyChangeURL)
if err != nil {
return err
}
res, err := c.post(ctx, nil, dir.KeyChangeURL, base64.RawURLEncoding.EncodeToString(inner), wantStatus(http.StatusOK))
if err != nil {
return err
}
defer res.Body.Close()
c.Key = newKey
return nil
}
// AuthorizeOrder initiates the order-based application for certificate issuance,
// as opposed to pre-authorization in Authorize.
// It is only supported by CAs implementing RFC 8555.
@@ -389,7 +351,7 @@ func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][
// Get all the bytes up to a sane maximum.
// Account very roughly for base64 overhead.
const max = maxCertChainSize + maxCertChainSize/33
b, err := io.ReadAll(io.LimitReader(res.Body, max+1))
b, err := ioutil.ReadAll(io.LimitReader(res.Body, max+1))
if err != nil {
return nil, fmt.Errorf("acme: fetch cert response stream: %v", err)
}
@@ -448,29 +410,3 @@ func isAlreadyRevoked(err error) bool {
e, ok := err.(*Error)
return ok && e.ProblemType == "urn:ietf:params:acme:error:alreadyRevoked"
}
// ListCertAlternates retrieves any alternate certificate chain URLs for the
// given certificate chain URL. These alternate URLs can be passed to FetchCert
// in order to retrieve the alternate certificate chains.
//
// If there are no alternate issuer certificate chains, a nil slice will be
// returned.
func (c *Client) ListCertAlternates(ctx context.Context, url string) ([]string, error) {
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
return nil, err
}
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
if err != nil {
return nil, err
}
defer res.Body.Close()
// We don't need the body but we need to discard it so we don't end up
// preventing keep-alive
if _, err := io.Copy(io.Discard, res.Body); err != nil {
return nil, fmt.Errorf("acme: cert alternates response stream: %v", err)
}
alts := linkHeader(res.Header, "alternate")
return alts, nil
}
+60 -193
View File
@@ -15,14 +15,12 @@ import (
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"sync"
"testing"
"time"
@@ -64,7 +62,7 @@ func TestRFC_Discover(t *testing.T) {
}`, nonce, reg, order, authz, revoke, keychange, metaTerms, metaWebsite, metaCAA)
}))
defer ts.Close()
c := &Client{DirectoryURL: ts.URL}
c := Client{DirectoryURL: ts.URL}
dir, err := c.Discover(context.Background())
if err != nil {
t.Fatal(err)
@@ -148,7 +146,7 @@ func TestRFC_postKID(t *testing.T) {
w.Header().Set("Location", "/account-1")
w.Write([]byte(`{"status":"valid"}`))
case "/post":
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
@@ -177,7 +175,8 @@ func TestRFC_postKID(t *testing.T) {
}))
defer ts.Close()
ctx := context.Background()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cl := &Client{
Key: testKey,
DirectoryURL: ts.URL,
@@ -193,13 +192,13 @@ func TestRFC_postKID(t *testing.T) {
t.Fatal(err)
}
defer res.Body.Close()
b, _ := io.ReadAll(res.Body) // don't care about err - just checking b
b, _ := ioutil.ReadAll(res.Body) // don't care about err - just checking b
if string(b) != "pong" {
t.Errorf("res.Body = %q; want pong", b)
}
}
// acmeServer simulates a subset of RFC 8555 compliant CA.
// acmeServer simulates a subset of RFC8555 compliant CA.
//
// TODO: We also have x/crypto/acme/autocert/acmetest and startACMEServerStub in autocert_test.go.
// It feels like this acmeServer is a sweet spot between usefulness and added complexity.
@@ -233,7 +232,6 @@ func (s *acmeServer) start() {
"newOrder": %q,
"newAuthz": %q,
"revokeCert": %q,
"keyChange": %q,
"meta": {"termsOfService": %q}
}`,
s.url("/acme/new-nonce"),
@@ -241,7 +239,6 @@ func (s *acmeServer) start() {
s.url("/acme/new-order"),
s.url("/acme/new-authz"),
s.url("/acme/revoke-cert"),
s.url("/acme/key-change"),
s.url("/terms"),
)
return
@@ -296,7 +293,7 @@ func TestRFC_Register(t *testing.T) {
"orders": %q
}`, email, s.url("/accounts/1/orders"))
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
@@ -315,7 +312,8 @@ func TestRFC_Register(t *testing.T) {
s.start()
defer s.close()
ctx := context.Background()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cl := &Client{
Key: testKeyEC,
DirectoryURL: s.url("/"),
@@ -346,7 +344,7 @@ func TestRFC_Register(t *testing.T) {
if !didPrompt {
t.Error("tos prompt wasn't called")
}
if v := cl.accountKID(ctx); v != KeyID(okAccount.URI) {
if v := cl.accountKID(ctx); v != keyID(okAccount.URI) {
t.Errorf("account kid = %q; want %q", v, okAccount.URI)
}
}
@@ -452,7 +450,8 @@ func TestRFC_RegisterExternalAccountBinding(t *testing.T) {
s.start()
defer s.close()
ctx := context.Background()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cl := &Client{
Key: testKeyEC,
DirectoryURL: s.url("/"),
@@ -483,7 +482,7 @@ func TestRFC_RegisterExternalAccountBinding(t *testing.T) {
if !didPrompt {
t.Error("tos prompt wasn't called")
}
if v := cl.accountKID(ctx); v != KeyID(okAccount.URI) {
if v := cl.accountKID(ctx); v != keyID(okAccount.URI) {
t.Errorf("account kid = %q; want %q", v, okAccount.URI)
}
}
@@ -503,7 +502,7 @@ func TestRFC_RegisterExisting(t *testing.T) {
if err != ErrAccountAlreadyExists {
t.Errorf("err = %v; want %v", err, ErrAccountAlreadyExists)
}
kid := KeyID(s.url("/accounts/1"))
kid := keyID(s.url("/accounts/1"))
if v := cl.accountKID(context.Background()); v != kid {
t.Errorf("account kid = %q; want %q", v, kid)
}
@@ -525,7 +524,7 @@ func TestRFC_UpdateReg(t *testing.T) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "valid"}`))
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
@@ -622,128 +621,6 @@ func TestRFC_GetRegOtherError(t *testing.T) {
}
}
func TestRFC_AccountKeyRollover(t *testing.T) {
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", s.url("/accounts/1"))
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "valid"}`))
})
s.handle("/acme/key-change", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
if err := cl.AccountKeyRollover(context.Background(), testKeyEC384); err != nil {
t.Errorf("AccountKeyRollover: %v, wanted no error", err)
} else if cl.Key != testKeyEC384 {
t.Error("AccountKeyRollover did not rotate the client key")
}
}
func TestRFC_DeactivateReg(t *testing.T) {
const email = "mailto:user@example.org"
curStatus := StatusValid
type account struct {
Status string `json:"status"`
Contact []string `json:"contact"`
AcceptTOS bool `json:"termsOfServiceAgreed"`
Orders string `json:"orders"`
}
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", s.url("/accounts/1"))
w.WriteHeader(http.StatusOK) // 200 means existing account
json.NewEncoder(w).Encode(account{
Status: curStatus,
Contact: []string{email},
AcceptTOS: true,
Orders: s.url("/accounts/1/orders"),
})
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
return
}
if len(head.JWK) == 0 {
t.Error("head.JWK is empty")
}
var req struct {
Status string `json:"status"`
Contact []string `json:"contact"`
AcceptTOS bool `json:"termsOfServiceAgreed"`
OnlyExisting bool `json:"onlyReturnExisting"`
}
decodeJWSRequest(t, &req, bytes.NewReader(b))
if !req.OnlyExisting {
t.Errorf("req.OnlyReturnExisting = %t; want = %t", req.OnlyExisting, true)
}
})
s.handle("/accounts/1", func(w http.ResponseWriter, r *http.Request) {
if curStatus == StatusValid {
curStatus = StatusDeactivated
w.WriteHeader(http.StatusOK)
} else {
s.error(w, &wireError{
Status: http.StatusUnauthorized,
Type: "urn:ietf:params:acme:error:unauthorized",
})
}
var req account
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
return
}
if len(head.JWK) != 0 {
t.Error("head.JWK is not empty")
}
if !strings.HasSuffix(head.KID, "/accounts/1") {
t.Errorf("head.KID = %q; want suffix /accounts/1", head.KID)
}
decodeJWSRequest(t, &req, bytes.NewReader(b))
if req.Status != StatusDeactivated {
t.Errorf("req.Status = %q; want = %q", req.Status, StatusDeactivated)
}
})
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
if err := cl.DeactivateReg(context.Background()); err != nil {
t.Errorf("DeactivateReg: %v, wanted no error", err)
}
if err := cl.DeactivateReg(context.Background()); err == nil {
t.Errorf("DeactivateReg: %v, wanted error for unauthorized", err)
}
}
func TestRF_DeactivateRegNoAccount(t *testing.T) {
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
s.error(w, &wireError{
Status: http.StatusBadRequest,
Type: "urn:ietf:params:acme:error:accountDoesNotExist",
})
})
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
if err := cl.DeactivateReg(context.Background()); !errors.Is(err, ErrNoAccount) {
t.Errorf("DeactivateReg: %v, wanted ErrNoAccount", err)
}
}
func TestRFC_AuthorizeOrder(t *testing.T) {
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
@@ -864,13 +741,24 @@ func testWaitOrderStatus(t *testing.T, okStatus string) {
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
order, err := cl.WaitOrder(context.Background(), s.url("/orders/1"))
if err != nil {
t.Fatalf("WaitOrder: %v", err)
}
if order.Status != okStatus {
t.Errorf("order.Status = %q; want %q", order.Status, okStatus)
var order *Order
var err error
done := make(chan struct{})
go func() {
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
order, err = cl.WaitOrder(context.Background(), s.url("/orders/1"))
close(done)
}()
select {
case <-time.After(3 * time.Second):
t.Fatal("WaitOrder took too long to return")
case <-done:
if err != nil {
t.Fatalf("WaitOrder: %v", err)
}
if order.Status != okStatus {
t.Errorf("order.Status = %q; want %q", order.Status, okStatus)
}
}
}
@@ -895,20 +783,30 @@ func TestRFC_WaitOrderError(t *testing.T) {
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
_, err := cl.WaitOrder(context.Background(), s.url("/orders/1"))
if err == nil {
t.Fatal("WaitOrder returned nil error")
}
e, ok := err.(*OrderError)
if !ok {
t.Fatalf("err = %v (%T); want OrderError", err, err)
}
if e.OrderURL != s.url("/orders/1") {
t.Errorf("e.OrderURL = %q; want %q", e.OrderURL, s.url("/orders/1"))
}
if e.Status != StatusInvalid {
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
var err error
done := make(chan struct{})
go func() {
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
_, err = cl.WaitOrder(context.Background(), s.url("/orders/1"))
close(done)
}()
select {
case <-time.After(3 * time.Second):
t.Fatal("WaitOrder took too long to return")
case <-done:
if err == nil {
t.Fatal("WaitOrder returned nil error")
}
e, ok := err.(*OrderError)
if !ok {
t.Fatalf("err = %v (%T); want OrderError", err, err)
}
if e.OrderURL != s.url("/orders/1") {
t.Errorf("e.OrderURL = %q; want %q", e.OrderURL, s.url("/orders/1"))
}
if e.Status != StatusInvalid {
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
}
}
}
@@ -948,7 +846,8 @@ func TestRFC_CreateOrderCert(t *testing.T) {
})
s.start()
defer s.close()
ctx := context.Background()
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
cert, curl, err := cl.CreateOrderCert(ctx, s.url("/pleaseissue"), csr, true)
@@ -983,35 +882,3 @@ func TestRFC_AlreadyRevokedCert(t *testing.T) {
t.Fatalf("RevokeCert: %v", err)
}
}
func TestRFC_ListCertAlternates(t *testing.T) {
s := newACMEServer()
s.handle("/crt", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/pem-certificate-chain")
w.Header().Add("Link", `<https://example.com/crt/2>;rel="alternate"`)
w.Header().Add("Link", `<https://example.com/crt/3>; rel="alternate"`)
w.Header().Add("Link", `<https://example.com/acme>; rel="index"`)
})
s.handle("/crt2", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/pem-certificate-chain")
})
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
crts, err := cl.ListCertAlternates(context.Background(), s.url("/crt"))
if err != nil {
t.Fatalf("ListCertAlternates: %v", err)
}
want := []string{"https://example.com/crt/2", "https://example.com/crt/3"}
if !reflect.DeepEqual(crts, want) {
t.Errorf("ListCertAlternates(/crt): %v; want %v", crts, want)
}
crts, err = cl.ListCertAlternates(context.Background(), s.url("/crt2"))
if err != nil {
t.Fatalf("ListCertAlternates: %v", err)
}
if crts != nil {
t.Errorf("ListCertAlternates(/crt2): %v; want nil", crts)
}
}
+10 -2
View File
@@ -297,7 +297,7 @@ type Directory struct {
// CAA consists of lowercase hostname elements, which the ACME server
// recognises as referring to itself for the purposes of CAA record validation
// as defined in RFC 6844.
// as defined in RFC6844.
CAA []string
// ExternalAccountRequired indicates that the CA requires for all account-related
@@ -305,6 +305,14 @@ type Directory struct {
ExternalAccountRequired bool
}
// rfcCompliant reports whether the ACME server implements RFC 8555.
// Note that some servers may have incomplete RFC implementation
// even if the returned value is true.
// If rfcCompliant reports false, the server most likely implements draft-02.
func (d *Directory) rfcCompliant() bool {
return d.OrderURL != ""
}
// Order represents a client's request for a certificate.
// It tracks the request flow progress through to issuance.
type Order struct {
@@ -440,7 +448,7 @@ func DomainIDs(names ...string) []AuthzID {
// IPIDs creates a slice of AuthzID with "ip" identifier type.
// Each element of addr is textual form of an address as defined
// in RFC 1123 Section 2.1 for IPv4 and in RFC 5952 Section 4 for IPv6.
// in RFC1123 Section 2.1 for IPv4 and in RFC5952 Section 4 for IPv6.
func IPIDs(addr ...string) []AuthzID {
a := make([]AuthzID, len(addr))
for i, v := range addr {
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.12
// +build go1.12
package acme
import "runtime/debug"
func init() {
// Set packageVersion if the binary was built in modules mode and x/crypto
// was not replaced with a different module.
info, ok := debug.ReadBuildInfo()
if !ok {
return
}
for _, m := range info.Deps {
if m.Path != "golang.org/x/crypto" {
continue
}
if m.Replace == nil {
packageVersion = m.Version
}
break
}
}
-287
View File
@@ -1,287 +0,0 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
. "github.com/mmcloughlin/avo/build"
. "github.com/mmcloughlin/avo/operand"
. "github.com/mmcloughlin/avo/reg"
_ "golang.org/x/crypto/argon2"
)
//go:generate go run . -out ../blamka_amd64.s -pkg argon2
func main() {
Package("golang.org/x/crypto/argon2")
ConstraintExpr("amd64,gc,!purego")
blamkaSSE4()
mixBlocksSSE2()
xorBlocksSSE2()
Generate()
}
func blamkaSSE4() {
Implement("blamkaSSE4")
Attributes(NOSPLIT)
AllocLocal(0)
Load(Param("b"), RAX)
c40 := c40_DATA()
c48 := c48_DATA()
MOVOU(c40, X10)
MOVOU(c48, X11)
BLAMKA_ROUND_0(AX, 0, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 16, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 32, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 48, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 64, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 80, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 96, X8, X9, X10, X11)
BLAMKA_ROUND_0(AX, 112, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 0, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 2, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 4, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 6, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 8, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 10, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 12, X8, X9, X10, X11)
BLAMKA_ROUND_1(AX, 14, X8, X9, X10, X11)
RET()
}
func mixBlocksSSE2() {
Implement("mixBlocksSSE2")
Attributes(NOSPLIT)
AllocLocal(0)
Load(Param("out"), RDX)
Load(Param("a"), RAX)
Load(Param("b"), RBX)
Load(Param("c"), RCX)
MOVQ(U32(128), RDI)
Label("loop")
MOVOU(Mem{Base: AX}.Offset(0), X0)
MOVOU(Mem{Base: BX}.Offset(0), X1)
MOVOU(Mem{Base: CX}.Offset(0), X2)
PXOR(X1, X0)
PXOR(X2, X0)
MOVOU(X0, Mem{Base: DX}.Offset(0))
ADDQ(Imm(16), RAX)
ADDQ(Imm(16), RBX)
ADDQ(Imm(16), RCX)
ADDQ(Imm(16), RDX)
SUBQ(Imm(2), RDI)
JA(LabelRef("loop"))
RET()
}
func xorBlocksSSE2() {
Implement("xorBlocksSSE2")
Attributes(NOSPLIT)
AllocLocal(0)
Load(Param("out"), RDX)
Load(Param("a"), RAX)
Load(Param("b"), RBX)
Load(Param("c"), RCX)
MOVQ(U32(128), RDI)
Label("loop")
MOVOU(Mem{Base: AX}.Offset(0), X0)
MOVOU(Mem{Base: BX}.Offset(0), X1)
MOVOU(Mem{Base: CX}.Offset(0), X2)
MOVOU(Mem{Base: DX}.Offset(0), X3)
PXOR(X1, X0)
PXOR(X2, X0)
PXOR(X3, X0)
MOVOU(X0, Mem{Base: DX}.Offset(0))
ADDQ(Imm(16), RAX)
ADDQ(Imm(16), RBX)
ADDQ(Imm(16), RCX)
ADDQ(Imm(16), RDX)
SUBQ(Imm(2), RDI)
JA(LabelRef("loop"))
RET()
}
func SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
MOVO(v4, t1)
MOVO(v5, v4)
MOVO(t1, v5)
MOVO(v6, t1)
PUNPCKLQDQ(v6, t2)
PUNPCKHQDQ(v7, v6)
PUNPCKHQDQ(t2, v6)
PUNPCKLQDQ(v7, t2)
MOVO(t1, v7)
MOVO(v2, t1)
PUNPCKHQDQ(t2, v7)
PUNPCKLQDQ(v3, t2)
PUNPCKHQDQ(t2, v2)
PUNPCKLQDQ(t1, t2)
PUNPCKHQDQ(t2, v3)
}
func SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
MOVO(v4, t1)
MOVO(v5, v4)
MOVO(t1, v5)
MOVO(v2, t1)
PUNPCKLQDQ(v2, t2)
PUNPCKHQDQ(v3, v2)
PUNPCKHQDQ(t2, v2)
PUNPCKLQDQ(v3, t2)
MOVO(t1, v3)
MOVO(v6, t1)
PUNPCKHQDQ(t2, v3)
PUNPCKLQDQ(v7, t2)
PUNPCKHQDQ(t2, v6)
PUNPCKLQDQ(t1, t2)
PUNPCKHQDQ(t2, v7)
}
func HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, c40, c48 VecPhysical) {
MOVO(v0, t0)
PMULULQ(v2, t0)
PADDQ(v2, v0)
PADDQ(t0, v0)
PADDQ(t0, v0)
PXOR(v0, v6)
PSHUFD(Imm(0xB1), v6, v6)
MOVO(v4, t0)
PMULULQ(v6, t0)
PADDQ(v6, v4)
PADDQ(t0, v4)
PADDQ(t0, v4)
PXOR(v4, v2)
PSHUFB(c40, v2)
MOVO(v0, t0)
PMULULQ(v2, t0)
PADDQ(v2, v0)
PADDQ(t0, v0)
PADDQ(t0, v0)
PXOR(v0, v6)
PSHUFB(c48, v6)
MOVO(v4, t0)
PMULULQ(v6, t0)
PADDQ(v6, v4)
PADDQ(t0, v4)
PADDQ(t0, v4)
PXOR(v4, v2)
MOVO(v2, t0)
PADDQ(v2, t0)
PSRLQ(Imm(63), v2)
PXOR(t0, v2)
MOVO(v1, t0)
PMULULQ(v3, t0)
PADDQ(v3, v1)
PADDQ(t0, v1)
PADDQ(t0, v1)
PXOR(v1, v7)
PSHUFD(Imm(0xB1), v7, v7)
MOVO(v5, t0)
PMULULQ(v7, t0)
PADDQ(v7, v5)
PADDQ(t0, v5)
PADDQ(t0, v5)
PXOR(v5, v3)
PSHUFB(c40, v3)
MOVO(v1, t0)
PMULULQ(v3, t0)
PADDQ(v3, v1)
PADDQ(t0, v1)
PADDQ(t0, v1)
PXOR(v1, v7)
PSHUFB(c48, v7)
MOVO(v5, t0)
PMULULQ(v7, t0)
PADDQ(v7, v5)
PADDQ(t0, v5)
PADDQ(t0, v5)
PXOR(v5, v3)
MOVO(v3, t0)
PADDQ(v3, t0)
PSRLQ(Imm(63), v3)
PXOR(t0, v3)
}
func LOAD_MSG_0(block GPPhysical, off int) {
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
for i, r := range registers {
MOVOU(Mem{Base: block}.Offset(8*(off+(i*2))), r)
}
}
func STORE_MSG_0(block GPPhysical, off int) {
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
for i, r := range registers {
MOVOU(r, Mem{Base: block}.Offset(8*(off+(i*2))))
}
}
func LOAD_MSG_1(block GPPhysical, off int) {
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
for i, r := range registers {
MOVOU(Mem{Base: block}.Offset(8*off+i*16*8), r)
}
}
func STORE_MSG_1(block GPPhysical, off int) {
var registers = []VecPhysical{X0, X1, X2, X3, X4, X5, X6, X7}
for i, r := range registers {
MOVOU(r, Mem{Base: block}.Offset(8*off+i*16*8))
}
}
func BLAMKA_ROUND_0(block GPPhysical, off int, t0, t1, c40, c48 VecPhysical) {
LOAD_MSG_0(block, off)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1)
STORE_MSG_0(block, off)
}
func BLAMKA_ROUND_1(block GPPhysical, off int, t0, t1, c40, c48 VecPhysical) {
LOAD_MSG_1(block, off)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1)
STORE_MSG_1(block, off)
}
// ##------------------DATA SECTION-------------------##
var c40_DATA_ptr, c48_DATA_ptr *Mem
func c40_DATA() Mem {
if c40_DATA_ptr != nil {
return *c40_DATA_ptr
}
c40_DATA := GLOBL("·c40", NOPTR|RODATA)
c40_DATA_ptr = &c40_DATA
DATA(0x00, U64(0x0201000706050403))
DATA(0x08, U64(0x0a09080f0e0d0c0b))
return c40_DATA
}
func c48_DATA() Mem {
if c48_DATA_ptr != nil {
return *c48_DATA_ptr
}
c48_DATA := GLOBL("·c48", NOPTR|RODATA)
c48_DATA_ptr = &c48_DATA
DATA(0x00, U64(0x0100070605040302))
DATA(0x08, U64(0x09080f0e0d0c0b0a))
return c48_DATA
}
-15
View File
@@ -1,15 +0,0 @@
module argon2/_asm
go 1.23
require (
github.com/mmcloughlin/avo v0.6.0
golang.org/x/crypto v0.26.0
)
require (
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)
-12
View File
@@ -1,12 +0,0 @@
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+6 -4
View File
@@ -11,7 +11,8 @@
// If you aren't sure which function you need, use Argon2id (IDKey) and
// the parameter recommendations for your scenario.
//
// # Argon2i
//
// Argon2i
//
// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
// It uses data-independent memory access, which is preferred for password
@@ -20,7 +21,8 @@
// parameters (taken from [2]) for non-interactive operations are time=3 and to
// use the maximum available memory.
//
// # Argon2id
//
// Argon2id
//
// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
// Argon2i and Argon2d. It uses data-independent memory access for the first
@@ -57,7 +59,7 @@ const (
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
//
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
// If using that amount of memory (32 MB) is not possible in some contexts then
@@ -81,7 +83,7 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
//
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
// If using that amount of memory (64 MB) is not possible in some contexts then
+1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package argon2
+216 -2764
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build !amd64 || purego || !gc
// +build !amd64 purego !gc
package argon2
+2 -11
View File
@@ -4,7 +4,7 @@
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
package bcrypt
package bcrypt // import "golang.org/x/crypto/bcrypt"
// The code is a port of Provos and Mazières's C implementation.
import (
@@ -50,7 +50,7 @@ func (ih InvalidHashPrefixError) Error() string {
type InvalidCostError int
func (ic InvalidCostError) Error() string {
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), MinCost, MaxCost)
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
}
const (
@@ -82,20 +82,11 @@ type hashed struct {
minor byte
}
// ErrPasswordTooLong is returned when the password passed to
// GenerateFromPassword is too long (i.e. > 72 bytes).
var ErrPasswordTooLong = errors.New("bcrypt: password length exceeds 72 bytes")
// GenerateFromPassword returns the bcrypt hash of the password at the given
// cost. If the cost given is less than MinCost, the cost will be set to
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
// to compare the returned hashed password with its cleartext version.
// GenerateFromPassword does not accept passwords longer than 72 bytes, which
// is the longest password bcrypt will operate on.
func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
if len(password) > 72 {
return nil, ErrPasswordTooLong
}
p, err := newFromPassword(password, cost)
if err != nil {
return nil, err
-7
View File
@@ -241,10 +241,3 @@ func TestNoSideEffectsFromCompare(t *testing.T) {
t.Errorf("got=%q want=%q", got, want)
}
}
func TestPasswordTooLong(t *testing.T) {
_, err := GenerateFromPassword(make([]byte, 73), 1)
if err != ErrPasswordTooLong {
t.Errorf("unexpected error: got %q, want %q", err, ErrPasswordTooLong)
}
}
File diff suppressed because it is too large Load Diff
-16
View File
@@ -1,16 +0,0 @@
module blake2b/_asm/AVX2
go 1.23
require (
github.com/mmcloughlin/avo v0.6.0
golang.org/x/crypto v0.26.0
)
require (
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)
-12
View File
@@ -1,12 +0,0 @@
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
-361
View File
@@ -1,361 +0,0 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
. "github.com/mmcloughlin/avo/build"
. "github.com/mmcloughlin/avo/operand"
. "github.com/mmcloughlin/avo/reg"
_ "golang.org/x/crypto/blake2b"
)
//go:generate go run . -out ../../blake2b_amd64.s -pkg blake2b
const ThatPeskyUnicodeDot = "\u00b7"
var iv0_DATA_ptr, iv1_DATA_ptr, iv2_DATA_ptr, iv3_DATA_ptr, c40_DATA_ptr, c48_DATA_ptr *Mem
func main() {
Package("golang.org/x/crypto/blake2b")
ConstraintExpr("amd64,gc,!purego")
hashBlocksSSE4()
Generate()
}
func SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
MOVO(v4, t1)
MOVO(v5, v4)
MOVO(t1, v5)
MOVO(v6, t1)
PUNPCKLQDQ(v6, t2)
PUNPCKHQDQ(v7, v6)
PUNPCKHQDQ(t2, v6)
PUNPCKLQDQ(v7, t2)
MOVO(t1, v7)
MOVO(v2, t1)
PUNPCKHQDQ(t2, v7)
PUNPCKLQDQ(v3, t2)
PUNPCKHQDQ(t2, v2)
PUNPCKLQDQ(t1, t2)
PUNPCKHQDQ(t2, v3)
}
func SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2 VecPhysical) {
MOVO(v4, t1)
MOVO(v5, v4)
MOVO(t1, v5)
MOVO(v2, t1)
PUNPCKLQDQ(v2, t2)
PUNPCKHQDQ(v3, v2)
PUNPCKHQDQ(t2, v2)
PUNPCKLQDQ(v3, t2)
MOVO(t1, v3)
MOVO(v6, t1)
PUNPCKHQDQ(t2, v3)
PUNPCKLQDQ(v7, t2)
PUNPCKHQDQ(t2, v6)
PUNPCKLQDQ(t1, t2)
PUNPCKHQDQ(t2, v7)
}
func HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7 VecPhysical, m0, m1, m2, m3 Op, t0, c40, c48 VecPhysical) {
PADDQ(m0, v0)
PADDQ(m1, v1)
PADDQ(v2, v0)
PADDQ(v3, v1)
PXOR(v0, v6)
PXOR(v1, v7)
PSHUFD(Imm(0xB1), v6, v6)
PSHUFD(Imm(0xB1), v7, v7)
PADDQ(v6, v4)
PADDQ(v7, v5)
PXOR(v4, v2)
PXOR(v5, v3)
PSHUFB(c40, v2)
PSHUFB(c40, v3)
PADDQ(m2, v0)
PADDQ(m3, v1)
PADDQ(v2, v0)
PADDQ(v3, v1)
PXOR(v0, v6)
PXOR(v1, v7)
PSHUFB(c48, v6)
PSHUFB(c48, v7)
PADDQ(v6, v4)
PADDQ(v7, v5)
PXOR(v4, v2)
PXOR(v5, v3)
MOVOU(v2, t0)
PADDQ(v2, t0)
PSRLQ(Imm(63), v2)
PXOR(t0, v2)
MOVOU(v3, t0)
PADDQ(v3, t0)
PSRLQ(Imm(63), v3)
PXOR(t0, v3)
}
func LOAD_MSG(m0, m1, m2, m3 VecPhysical, src GPPhysical, i0, i1, i2, i3, i4, i5, i6, i7 int) {
MOVQ(Mem{Base: src}.Offset(i0*8), m0)
PINSRQ(Imm(1), Mem{Base: src}.Offset(i1*8), m0)
MOVQ(Mem{Base: src}.Offset(i2*8), m1)
PINSRQ(Imm(1), Mem{Base: src}.Offset(i3*8), m1)
MOVQ(Mem{Base: src}.Offset(i4*8), m2)
PINSRQ(Imm(1), Mem{Base: src}.Offset(i5*8), m2)
MOVQ(Mem{Base: src}.Offset(i6*8), m3)
PINSRQ(Imm(1), Mem{Base: src}.Offset(i7*8), m3)
}
func hashBlocksSSE4() {
Implement("hashBlocksSSE4")
Attributes(4)
AllocLocal(288) // frame size = 272 + 16 byte alignment
Load(Param("h"), RAX)
Load(Param("c"), RBX)
Load(Param("flag"), RCX)
Load(Param("blocks").Base(), RSI)
Load(Param("blocks").Len(), RDI)
MOVQ(RSP, R10)
ADDQ(Imm(15), R10)
ANDQ(I32(-16), R10)
iv3 := iv3_DATA()
MOVOU(iv3, X0)
MOVO(X0, Mem{Base: R10}.Offset(0))
XORQ(RCX, Mem{Base: R10}.Offset(0)) // 0(R10) = ·iv3 ^ (CX || 0)
c40 := c40_DATA()
c48 := c48_DATA()
MOVOU(c40, X13)
MOVOU(c48, X14)
MOVOU(Mem{Base: AX}.Offset(0), X12)
MOVOU(Mem{Base: AX}.Offset(16), X15)
MOVQ(Mem{Base: BX}.Offset(0), R8)
MOVQ(Mem{Base: BX}.Offset(8), R9)
Label("loop")
ADDQ(Imm(128), R8)
CMPQ(R8, Imm(128))
JGE(LabelRef("noinc"))
INCQ(R9)
Label("noinc")
MOVQ(R8, X8)
PINSRQ(Imm(1), R9, X8)
iv0 := iv0_DATA()
iv1 := iv1_DATA()
iv2 := iv2_DATA()
MOVO(X12, X0)
MOVO(X15, X1)
MOVOU(Mem{Base: AX}.Offset(32), X2)
MOVOU(Mem{Base: AX}.Offset(48), X3)
MOVOU(iv0, X4)
MOVOU(iv1, X5)
MOVOU(iv2, X6)
PXOR(X8, X6)
MOVO(Mem{Base: R10}.Offset(0), X7)
LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7)
MOVO(X8, Mem{Base: R10}.Offset(16))
MOVO(X9, Mem{Base: R10}.Offset(32))
MOVO(X10, Mem{Base: R10}.Offset(48))
MOVO(X11, Mem{Base: R10}.Offset(64))
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15)
MOVO(X8, Mem{Base: R10}.Offset(80))
MOVO(X9, Mem{Base: R10}.Offset(96))
MOVO(X10, Mem{Base: R10}.Offset(112))
MOVO(X11, Mem{Base: R10}.Offset(128))
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6)
MOVO(X8, Mem{Base: R10}.Offset(144))
MOVO(X9, Mem{Base: R10}.Offset(160))
MOVO(X10, Mem{Base: R10}.Offset(176))
MOVO(X11, Mem{Base: R10}.Offset(192))
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3)
MOVO(X8, Mem{Base: R10}.Offset(208))
MOVO(X9, Mem{Base: R10}.Offset(224))
MOVO(X10, Mem{Base: R10}.Offset(240))
MOVO(X11, Mem{Base: R10}.Offset(256))
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 10, 3, 7, 9, 14, 6, 1, 4)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 2, 5, 4, 15, 6, 10, 0, 8)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 14, 11, 6, 3, 1, 12, 8, 13)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 4, 7, 15, 1, 13, 5, 14, 9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 0, 6, 9, 8, 7, 3, 2, 11)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 5, 15, 8, 2, 0, 4, 6, 10)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 12, 13, 1, 10, 2, 7, 4, 5)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
LOAD_MSG(X8, X9, X10, X11, SI, 15, 9, 3, 13, 11, 14, 12, 0)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(16), Mem{Base: R10}.Offset(32), Mem{Base: R10}.Offset(48), Mem{Base: R10}.Offset(64), X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(80), Mem{Base: R10}.Offset(96), Mem{Base: R10}.Offset(112), Mem{Base: R10}.Offset(128), X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(144), Mem{Base: R10}.Offset(160), Mem{Base: R10}.Offset(176), Mem{Base: R10}.Offset(192), X11, X13, X14)
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, Mem{Base: R10}.Offset(208), Mem{Base: R10}.Offset(224), Mem{Base: R10}.Offset(240), Mem{Base: R10}.Offset(256), X11, X13, X14)
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
MOVOU(Mem{Base: AX}.Offset(32), X10)
MOVOU(Mem{Base: AX}.Offset(48), X11)
PXOR(X0, X12)
PXOR(X1, X15)
PXOR(X2, X10)
PXOR(X3, X11)
PXOR(X4, X12)
PXOR(X5, X15)
PXOR(X6, X10)
PXOR(X7, X11)
MOVOU(X10, Mem{Base: AX}.Offset(32))
MOVOU(X11, Mem{Base: AX}.Offset(48))
LEAQ(Mem{Base: SI}.Offset(128), RSI)
SUBQ(Imm(128), RDI)
JNE(LabelRef("loop"))
MOVOU(X12, Mem{Base: AX}.Offset(0))
MOVOU(X15, Mem{Base: AX}.Offset(16))
MOVQ(R8, Mem{Base: BX}.Offset(0))
MOVQ(R9, Mem{Base: BX}.Offset(8))
RET()
}
// #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DATA SECTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
func iv0_DATA() Mem {
if iv0_DATA_ptr != nil {
return *iv0_DATA_ptr
}
iv0 := GLOBL(ThatPeskyUnicodeDot+"iv0", NOPTR|RODATA)
iv0_DATA_ptr = &iv0
DATA(0x00, U64(0x6a09e667f3bcc908))
DATA(0x08, U64(0xbb67ae8584caa73b))
return iv0
}
func iv1_DATA() Mem {
if iv1_DATA_ptr != nil {
return *iv1_DATA_ptr
}
iv1 := GLOBL(ThatPeskyUnicodeDot+"iv1", NOPTR|RODATA)
iv1_DATA_ptr = &iv1
DATA(0x00, U64(0x3c6ef372fe94f82b))
DATA(0x08, U64(0xa54ff53a5f1d36f1))
return iv1
}
func iv2_DATA() Mem {
if iv2_DATA_ptr != nil {
return *iv2_DATA_ptr
}
iv2 := GLOBL(ThatPeskyUnicodeDot+"iv2", NOPTR|RODATA)
iv2_DATA_ptr = &iv2
DATA(0x00, U64(0x510e527fade682d1))
DATA(0x08, U64(0x9b05688c2b3e6c1f))
return iv2
}
func iv3_DATA() Mem {
if iv3_DATA_ptr != nil {
return *iv3_DATA_ptr
}
iv3 := GLOBL(ThatPeskyUnicodeDot+"iv3", NOPTR|RODATA)
iv3_DATA_ptr = &iv3
DATA(0x00, U64(0x1f83d9abfb41bd6b))
DATA(0x08, U64(0x5be0cd19137e2179))
return iv3
}
func c40_DATA() Mem {
if c40_DATA_ptr != nil {
return *c40_DATA_ptr
}
c40 := GLOBL(ThatPeskyUnicodeDot+"c40", NOPTR|RODATA)
c40_DATA_ptr = &c40
DATA(0x00, U64(0x0201000706050403))
DATA(0x08, U64(0x0a09080f0e0d0c0b))
return c40
}
func c48_DATA() Mem {
if c48_DATA_ptr != nil {
return *c48_DATA_ptr
}
c48 := GLOBL(ThatPeskyUnicodeDot+"c48", NOPTR|RODATA)
c48_DATA_ptr = &c48
DATA(0x00, U64(0x0100070605040302))
DATA(0x08, U64(0x09080f0e0d0c0b0a))
return c48
}
-15
View File
@@ -1,15 +0,0 @@
module blake2b/_asm
go 1.23
require (
github.com/mmcloughlin/avo v0.6.0
golang.org/x/crypto v0.26.0
)
require (
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)
-12
View File
@@ -1,12 +0,0 @@
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+2 -1
View File
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && gc && !purego
//go:build go1.7 && amd64 && gc && !purego
// +build go1.7,amd64,gc,!purego
package blake2b
+678 -4493
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.7 && amd64 && gc && !purego
// +build !go1.7,amd64,gc,!purego
package blake2b
import "golang.org/x/sys/cpu"
func init() {
useSSE4 = cpu.X86.HasSSE41
}
//go:noescape
func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
if useSSE4 {
hashBlocksSSE4(h, c, flag, blocks)
} else {
hashBlocksGeneric(h, c, flag, blocks)
}
}
+265 -1428
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build !amd64 || purego || !gc
// +build !amd64 purego !gc
package blake2b
+3
View File
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.9
// +build go1.9
package blake2b
import (
-525
View File
@@ -1,525 +0,0 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
. "github.com/mmcloughlin/avo/build"
"github.com/mmcloughlin/avo/ir"
. "github.com/mmcloughlin/avo/operand"
. "github.com/mmcloughlin/avo/reg"
_ "golang.org/x/crypto/blake2s"
)
//go:generate go run . -out ../blake2s_amd64.s -pkg blake2s
func main() {
Package("golang.org/x/crypto/blake2s")
ConstraintExpr("amd64,gc,!purego")
hashBlocksSSE2()
hashBlocksSSSE3()
hashBlocksSSE4()
Generate()
}
func ROTL_SSE2(n uint64, t, v VecPhysical) {
MOVO(v, t)
PSLLL(Imm(n), t)
PSRLL(Imm(32-n), v)
PXOR(t, v)
}
func ROTL_SSSE3(c, v VecPhysical) {
PSHUFB(c, v)
}
func ROUND_SSE2(v0, v1, v2, v3 VecPhysical, m0, m1, m2, m3 Mem, t VecPhysical) {
PADDL(m0, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSE2(16, t, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(20, t, v1)
PADDL(m1, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSE2(24, t, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(25, t, v1)
PSHUFL(Imm(0x39), v1, v1)
PSHUFL(Imm(0x4E), v2, v2)
PSHUFL(Imm(0x93), v3, v3)
PADDL(m2, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSE2(16, t, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(20, t, v1)
PADDL(m3, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSE2(24, t, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(25, t, v1)
PSHUFL(Imm(0x39), v3, v3)
PSHUFL(Imm(0x4E), v2, v2)
PSHUFL(Imm(0x93), v1, v1)
}
func ROUND_SSSE3(v0, v1, v2, v3 VecPhysical, m0, m1, m2, m3 Op, t, c16, c8 VecPhysical) {
PADDL(m0, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSSE3(c16, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(20, t, v1)
PADDL(m1, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSSE3(c8, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(25, t, v1)
PSHUFL(Imm(0x39), v1, v1)
PSHUFL(Imm(0x4E), v2, v2)
PSHUFL(Imm(0x93), v3, v3)
PADDL(m2, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSSE3(c16, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(20, t, v1)
PADDL(m3, v0)
PADDL(v1, v0)
PXOR(v0, v3)
ROTL_SSSE3(c8, v3)
PADDL(v3, v2)
PXOR(v2, v1)
ROTL_SSE2(25, t, v1)
PSHUFL(Imm(0x39), v3, v3)
PSHUFL(Imm(0x4E), v2, v2)
PSHUFL(Imm(0x93), v1, v1)
}
func LOAD_MSG_SSE4(m0, m1, m2, m3 VecPhysical, src GPPhysical, i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 int) {
// Hack to get Avo to emit a MOVL instruction with a VecPhysical as the destination
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i0 * 4), m0}})
PINSRD(Imm(1), Mem{Base: src}.Offset(i1*4), m0)
PINSRD(Imm(2), Mem{Base: src}.Offset(i2*4), m0)
PINSRD(Imm(3), Mem{Base: src}.Offset(i3*4), m0)
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i4 * 4), m1}})
PINSRD(Imm(1), Mem{Base: src}.Offset(i5*4), m1)
PINSRD(Imm(2), Mem{Base: src}.Offset(i6*4), m1)
PINSRD(Imm(3), Mem{Base: src}.Offset(i7*4), m1)
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i8 * 4), m2}})
PINSRD(Imm(1), Mem{Base: src}.Offset(i9*4), m2)
PINSRD(Imm(2), Mem{Base: src}.Offset(i10*4), m2)
PINSRD(Imm(3), Mem{Base: src}.Offset(i11*4), m2)
Instruction(&ir.Instruction{Opcode: "MOVL", Operands: []Op{Mem{Base: src}.Offset(i12 * 4), m3}})
PINSRD(Imm(1), Mem{Base: src}.Offset(i13*4), m3)
PINSRD(Imm(2), Mem{Base: src}.Offset(i14*4), m3)
PINSRD(Imm(3), Mem{Base: src}.Offset(i15*4), m3)
}
func PRECOMPUTE_MSG(dst GPPhysical, off int, src, R8, R9, R10, R11, R12, R13, R14, R15 GPPhysical) {
MOVQ(Mem{Base: src}.Offset(0*4), R8)
MOVQ(Mem{Base: src}.Offset(2*4), R9)
MOVQ(Mem{Base: src}.Offset(4*4), R10)
MOVQ(Mem{Base: src}.Offset(6*4), R11)
MOVQ(Mem{Base: src}.Offset(8*4), R12)
MOVQ(Mem{Base: src}.Offset(10*4), R13)
MOVQ(Mem{Base: src}.Offset(12*4), R14)
MOVQ(Mem{Base: src}.Offset(14*4), R15)
MOVL(R8L, Mem{Base: dst}.Offset(0*4+off+0))
MOVL(R8L, Mem{Base: dst}.Offset(9*4+off+64))
MOVL(R8L, Mem{Base: dst}.Offset(5*4+off+128))
MOVL(R8L, Mem{Base: dst}.Offset(14*4+off+192))
MOVL(R8L, Mem{Base: dst}.Offset(4*4+off+256))
MOVL(R8L, Mem{Base: dst}.Offset(2*4+off+320))
MOVL(R8L, Mem{Base: dst}.Offset(8*4+off+384))
MOVL(R8L, Mem{Base: dst}.Offset(12*4+off+448))
MOVL(R8L, Mem{Base: dst}.Offset(3*4+off+512))
MOVL(R8L, Mem{Base: dst}.Offset(15*4+off+576))
SHRQ(Imm(32), R8)
MOVL(R8L, Mem{Base: dst}.Offset(4*4+off+0))
MOVL(R8L, Mem{Base: dst}.Offset(8*4+off+64))
MOVL(R8L, Mem{Base: dst}.Offset(14*4+off+128))
MOVL(R8L, Mem{Base: dst}.Offset(5*4+off+192))
MOVL(R8L, Mem{Base: dst}.Offset(12*4+off+256))
MOVL(R8L, Mem{Base: dst}.Offset(11*4+off+320))
MOVL(R8L, Mem{Base: dst}.Offset(1*4+off+384))
MOVL(R8L, Mem{Base: dst}.Offset(6*4+off+448))
MOVL(R8L, Mem{Base: dst}.Offset(10*4+off+512))
MOVL(R8L, Mem{Base: dst}.Offset(3*4+off+576))
MOVL(R9L, Mem{Base: dst}.Offset(1*4+off+0))
MOVL(R9L, Mem{Base: dst}.Offset(13*4+off+64))
MOVL(R9L, Mem{Base: dst}.Offset(6*4+off+128))
MOVL(R9L, Mem{Base: dst}.Offset(8*4+off+192))
MOVL(R9L, Mem{Base: dst}.Offset(2*4+off+256))
MOVL(R9L, Mem{Base: dst}.Offset(0*4+off+320))
MOVL(R9L, Mem{Base: dst}.Offset(14*4+off+384))
MOVL(R9L, Mem{Base: dst}.Offset(11*4+off+448))
MOVL(R9L, Mem{Base: dst}.Offset(12*4+off+512))
MOVL(R9L, Mem{Base: dst}.Offset(4*4+off+576))
SHRQ(Imm(32), R9)
MOVL(R9L, Mem{Base: dst}.Offset(5*4+off+0))
MOVL(R9L, Mem{Base: dst}.Offset(15*4+off+64))
MOVL(R9L, Mem{Base: dst}.Offset(9*4+off+128))
MOVL(R9L, Mem{Base: dst}.Offset(1*4+off+192))
MOVL(R9L, Mem{Base: dst}.Offset(11*4+off+256))
MOVL(R9L, Mem{Base: dst}.Offset(7*4+off+320))
MOVL(R9L, Mem{Base: dst}.Offset(13*4+off+384))
MOVL(R9L, Mem{Base: dst}.Offset(3*4+off+448))
MOVL(R9L, Mem{Base: dst}.Offset(6*4+off+512))
MOVL(R9L, Mem{Base: dst}.Offset(10*4+off+576))
MOVL(R10L, Mem{Base: dst}.Offset(2*4+off+0))
MOVL(R10L, Mem{Base: dst}.Offset(1*4+off+64))
MOVL(R10L, Mem{Base: dst}.Offset(15*4+off+128))
MOVL(R10L, Mem{Base: dst}.Offset(10*4+off+192))
MOVL(R10L, Mem{Base: dst}.Offset(6*4+off+256))
MOVL(R10L, Mem{Base: dst}.Offset(8*4+off+320))
MOVL(R10L, Mem{Base: dst}.Offset(3*4+off+384))
MOVL(R10L, Mem{Base: dst}.Offset(13*4+off+448))
MOVL(R10L, Mem{Base: dst}.Offset(14*4+off+512))
MOVL(R10L, Mem{Base: dst}.Offset(5*4+off+576))
SHRQ(Imm(32), R10)
MOVL(R10L, Mem{Base: dst}.Offset(6*4+off+0))
MOVL(R10L, Mem{Base: dst}.Offset(11*4+off+64))
MOVL(R10L, Mem{Base: dst}.Offset(2*4+off+128))
MOVL(R10L, Mem{Base: dst}.Offset(9*4+off+192))
MOVL(R10L, Mem{Base: dst}.Offset(1*4+off+256))
MOVL(R10L, Mem{Base: dst}.Offset(13*4+off+320))
MOVL(R10L, Mem{Base: dst}.Offset(4*4+off+384))
MOVL(R10L, Mem{Base: dst}.Offset(8*4+off+448))
MOVL(R10L, Mem{Base: dst}.Offset(15*4+off+512))
MOVL(R10L, Mem{Base: dst}.Offset(7*4+off+576))
MOVL(R11L, Mem{Base: dst}.Offset(3*4+off+0))
MOVL(R11L, Mem{Base: dst}.Offset(7*4+off+64))
MOVL(R11L, Mem{Base: dst}.Offset(13*4+off+128))
MOVL(R11L, Mem{Base: dst}.Offset(12*4+off+192))
MOVL(R11L, Mem{Base: dst}.Offset(10*4+off+256))
MOVL(R11L, Mem{Base: dst}.Offset(1*4+off+320))
MOVL(R11L, Mem{Base: dst}.Offset(9*4+off+384))
MOVL(R11L, Mem{Base: dst}.Offset(14*4+off+448))
MOVL(R11L, Mem{Base: dst}.Offset(0*4+off+512))
MOVL(R11L, Mem{Base: dst}.Offset(6*4+off+576))
SHRQ(Imm(32), R11)
MOVL(R11L, Mem{Base: dst}.Offset(7*4+off+0))
MOVL(R11L, Mem{Base: dst}.Offset(14*4+off+64))
MOVL(R11L, Mem{Base: dst}.Offset(10*4+off+128))
MOVL(R11L, Mem{Base: dst}.Offset(0*4+off+192))
MOVL(R11L, Mem{Base: dst}.Offset(5*4+off+256))
MOVL(R11L, Mem{Base: dst}.Offset(9*4+off+320))
MOVL(R11L, Mem{Base: dst}.Offset(12*4+off+384))
MOVL(R11L, Mem{Base: dst}.Offset(1*4+off+448))
MOVL(R11L, Mem{Base: dst}.Offset(13*4+off+512))
MOVL(R11L, Mem{Base: dst}.Offset(2*4+off+576))
MOVL(R12L, Mem{Base: dst}.Offset(8*4+off+0))
MOVL(R12L, Mem{Base: dst}.Offset(5*4+off+64))
MOVL(R12L, Mem{Base: dst}.Offset(4*4+off+128))
MOVL(R12L, Mem{Base: dst}.Offset(15*4+off+192))
MOVL(R12L, Mem{Base: dst}.Offset(14*4+off+256))
MOVL(R12L, Mem{Base: dst}.Offset(3*4+off+320))
MOVL(R12L, Mem{Base: dst}.Offset(11*4+off+384))
MOVL(R12L, Mem{Base: dst}.Offset(10*4+off+448))
MOVL(R12L, Mem{Base: dst}.Offset(7*4+off+512))
MOVL(R12L, Mem{Base: dst}.Offset(1*4+off+576))
SHRQ(Imm(32), R12)
MOVL(R12L, Mem{Base: dst}.Offset(12*4+off+0))
MOVL(R12L, Mem{Base: dst}.Offset(2*4+off+64))
MOVL(R12L, Mem{Base: dst}.Offset(11*4+off+128))
MOVL(R12L, Mem{Base: dst}.Offset(4*4+off+192))
MOVL(R12L, Mem{Base: dst}.Offset(0*4+off+256))
MOVL(R12L, Mem{Base: dst}.Offset(15*4+off+320))
MOVL(R12L, Mem{Base: dst}.Offset(10*4+off+384))
MOVL(R12L, Mem{Base: dst}.Offset(7*4+off+448))
MOVL(R12L, Mem{Base: dst}.Offset(5*4+off+512))
MOVL(R12L, Mem{Base: dst}.Offset(9*4+off+576))
MOVL(R13L, Mem{Base: dst}.Offset(9*4+off+0))
MOVL(R13L, Mem{Base: dst}.Offset(4*4+off+64))
MOVL(R13L, Mem{Base: dst}.Offset(8*4+off+128))
MOVL(R13L, Mem{Base: dst}.Offset(13*4+off+192))
MOVL(R13L, Mem{Base: dst}.Offset(3*4+off+256))
MOVL(R13L, Mem{Base: dst}.Offset(5*4+off+320))
MOVL(R13L, Mem{Base: dst}.Offset(7*4+off+384))
MOVL(R13L, Mem{Base: dst}.Offset(15*4+off+448))
MOVL(R13L, Mem{Base: dst}.Offset(11*4+off+512))
MOVL(R13L, Mem{Base: dst}.Offset(0*4+off+576))
SHRQ(Imm(32), R13)
MOVL(R13L, Mem{Base: dst}.Offset(13*4+off+0))
MOVL(R13L, Mem{Base: dst}.Offset(10*4+off+64))
MOVL(R13L, Mem{Base: dst}.Offset(0*4+off+128))
MOVL(R13L, Mem{Base: dst}.Offset(3*4+off+192))
MOVL(R13L, Mem{Base: dst}.Offset(9*4+off+256))
MOVL(R13L, Mem{Base: dst}.Offset(6*4+off+320))
MOVL(R13L, Mem{Base: dst}.Offset(15*4+off+384))
MOVL(R13L, Mem{Base: dst}.Offset(4*4+off+448))
MOVL(R13L, Mem{Base: dst}.Offset(2*4+off+512))
MOVL(R13L, Mem{Base: dst}.Offset(12*4+off+576))
MOVL(R14L, Mem{Base: dst}.Offset(10*4+off+0))
MOVL(R14L, Mem{Base: dst}.Offset(12*4+off+64))
MOVL(R14L, Mem{Base: dst}.Offset(1*4+off+128))
MOVL(R14L, Mem{Base: dst}.Offset(6*4+off+192))
MOVL(R14L, Mem{Base: dst}.Offset(13*4+off+256))
MOVL(R14L, Mem{Base: dst}.Offset(4*4+off+320))
MOVL(R14L, Mem{Base: dst}.Offset(0*4+off+384))
MOVL(R14L, Mem{Base: dst}.Offset(2*4+off+448))
MOVL(R14L, Mem{Base: dst}.Offset(8*4+off+512))
MOVL(R14L, Mem{Base: dst}.Offset(14*4+off+576))
SHRQ(Imm(32), R14)
MOVL(R14L, Mem{Base: dst}.Offset(14*4+off+0))
MOVL(R14L, Mem{Base: dst}.Offset(3*4+off+64))
MOVL(R14L, Mem{Base: dst}.Offset(7*4+off+128))
MOVL(R14L, Mem{Base: dst}.Offset(2*4+off+192))
MOVL(R14L, Mem{Base: dst}.Offset(15*4+off+256))
MOVL(R14L, Mem{Base: dst}.Offset(12*4+off+320))
MOVL(R14L, Mem{Base: dst}.Offset(6*4+off+384))
MOVL(R14L, Mem{Base: dst}.Offset(0*4+off+448))
MOVL(R14L, Mem{Base: dst}.Offset(9*4+off+512))
MOVL(R14L, Mem{Base: dst}.Offset(11*4+off+576))
MOVL(R15L, Mem{Base: dst}.Offset(11*4+off+0))
MOVL(R15L, Mem{Base: dst}.Offset(0*4+off+64))
MOVL(R15L, Mem{Base: dst}.Offset(12*4+off+128))
MOVL(R15L, Mem{Base: dst}.Offset(7*4+off+192))
MOVL(R15L, Mem{Base: dst}.Offset(8*4+off+256))
MOVL(R15L, Mem{Base: dst}.Offset(14*4+off+320))
MOVL(R15L, Mem{Base: dst}.Offset(2*4+off+384))
MOVL(R15L, Mem{Base: dst}.Offset(5*4+off+448))
MOVL(R15L, Mem{Base: dst}.Offset(1*4+off+512))
MOVL(R15L, Mem{Base: dst}.Offset(13*4+off+576))
SHRQ(Imm(32), R15)
MOVL(R15L, Mem{Base: dst}.Offset(15*4+off+0))
MOVL(R15L, Mem{Base: dst}.Offset(6*4+off+64))
MOVL(R15L, Mem{Base: dst}.Offset(3*4+off+128))
MOVL(R15L, Mem{Base: dst}.Offset(11*4+off+192))
MOVL(R15L, Mem{Base: dst}.Offset(7*4+off+256))
MOVL(R15L, Mem{Base: dst}.Offset(10*4+off+320))
MOVL(R15L, Mem{Base: dst}.Offset(5*4+off+384))
MOVL(R15L, Mem{Base: dst}.Offset(9*4+off+448))
MOVL(R15L, Mem{Base: dst}.Offset(4*4+off+512))
MOVL(R15L, Mem{Base: dst}.Offset(8*4+off+576))
}
func BLAKE2s_SSE2() {
PRECOMPUTE_MSG(BP, 16, SI, R8, R9, R10, R11, R12, R13, R14, R15)
for i := 0; i < 10; i++ {
ROUND_SSE2(X4, X5, X6, X7, Mem{Base: BP}.Offset(16+64*i), Mem{Base: BP}.Offset(32+64*i), Mem{Base: BP}.Offset(48+64*i), Mem{Base: BP}.Offset(64+64*i), X8)
}
}
func BLAKE2s_SSSE3() {
PRECOMPUTE_MSG(BP, 16, SI, R8, R9, R10, R11, R12, R13, R14, R15)
for i := 0; i < 10; i++ {
ROUND_SSSE3(X4, X5, X6, X7, Mem{Base: BP}.Offset(16+64*i), Mem{Base: BP}.Offset(32+64*i), Mem{Base: BP}.Offset(48+64*i), Mem{Base: BP}.Offset(64+64*i), X8, X13, X14)
}
}
func BLAKE2s_SSE4() {
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
LOAD_MSG_SSE4(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0)
ROUND_SSSE3(X4, X5, X6, X7, X8, X9, X10, X11, X8, X13, X14)
}
func HASH_BLOCKS(h, c, flag, blocks_base, blocks_len Mem, BLAKE2s_FUNC func()) {
MOVQ(h, RAX)
MOVQ(c, RBX)
MOVL(flag, ECX)
MOVQ(blocks_base, RSI)
MOVQ(blocks_len, RDX)
MOVQ(RSP, RBP)
ADDQ(Imm(15), RBP)
ANDQ(I32(^15), RBP)
MOVQ(Mem{Base: BX}.Offset(0), R9)
MOVQ(R9, Mem{Base: BP}.Offset(0))
MOVQ(RCX, Mem{Base: BP}.Offset(8))
MOVOU(Mem{Base: AX}.Offset(0), X0)
MOVOU(Mem{Base: AX}.Offset(16), X1)
iv0 := iv0_DATA()
iv1 := iv1_DATA()
MOVOU(iv0, X2)
MOVOU(iv1, X3)
counter := counter_DATA()
rol16 := rol16_DATA()
rol8 := rol8_DATA()
MOVOU(counter, X12)
MOVOU(rol16, X13)
MOVOU(rol8, X14)
MOVO(Mem{Base: BP}.Offset(0), X15)
Label("loop")
MOVO(X0, X4)
MOVO(X1, X5)
MOVO(X2, X6)
MOVO(X3, X7)
PADDQ(X12, X15)
PXOR(X15, X7)
BLAKE2s_FUNC()
PXOR(X4, X0)
PXOR(X5, X1)
PXOR(X6, X0)
PXOR(X7, X1)
LEAQ(Mem{Base: SI}.Offset(64), RSI)
SUBQ(Imm(64), RDX)
JNE(LabelRef("loop"))
MOVO(X15, Mem{Base: BP}.Offset(0))
MOVQ(Mem{Base: BP}.Offset(0), R9)
MOVQ(R9, Mem{Base: BX}.Offset(0))
MOVOU(X0, Mem{Base: AX}.Offset(0))
MOVOU(X1, Mem{Base: AX}.Offset(16))
}
func hashBlocksSSE2() {
Implement("hashBlocksSSE2")
Attributes(0)
AllocLocal(672) // frame = 656 + 16 byte alignment
h := NewParamAddr("h", 0)
c := NewParamAddr("c", 8)
flag := NewParamAddr("flag", 16)
blocks_base := NewParamAddr("blocks_base", 24)
blocks_len := NewParamAddr("blocks_len", 32)
HASH_BLOCKS(h, c, flag, blocks_base, blocks_len, BLAKE2s_SSE2)
RET()
}
func hashBlocksSSSE3() {
Implement("hashBlocksSSSE3")
Attributes(0)
AllocLocal(672) // frame = 656 + 16 byte alignment
h := NewParamAddr("h", 0)
c := NewParamAddr("c", 8)
flag := NewParamAddr("flag", 16)
blocks_base := NewParamAddr("blocks_base", 24)
blocks_len := NewParamAddr("blocks_len", 32)
HASH_BLOCKS(h, c, flag, blocks_base, blocks_len, BLAKE2s_SSSE3)
RET()
}
func hashBlocksSSE4() {
Implement("hashBlocksSSE4")
Attributes(0)
AllocLocal(32) // frame = 16 + 16 byte alignment
h := NewParamAddr("h", 0)
c := NewParamAddr("c", 8)
flag := NewParamAddr("flag", 16)
blocks_base := NewParamAddr("blocks_base", 24)
blocks_len := NewParamAddr("blocks_len", 32)
HASH_BLOCKS(h, c, flag, blocks_base, blocks_len, BLAKE2s_SSE4)
RET()
}
// ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DATA SECTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
var iv0_DATA_ptr, iv1_DATA_ptr, rol16_DATA_ptr, rol8_DATA_ptr, counter_DATA_ptr *Mem
func iv0_DATA() Mem {
if iv0_DATA_ptr != nil {
return *iv0_DATA_ptr
}
iv0_DATA := GLOBL("iv0", NOPTR|RODATA)
iv0_DATA_ptr = &iv0_DATA
DATA(0x00, U32(0x6a09e667))
DATA(0x04, U32(0xbb67ae85))
DATA(0x08, U32(0x3c6ef372))
DATA(0x0c, U32(0xa54ff53a))
return iv0_DATA
}
func iv1_DATA() Mem {
if iv1_DATA_ptr != nil {
return *iv1_DATA_ptr
}
iv1_DATA := GLOBL("iv1", NOPTR|RODATA)
iv1_DATA_ptr = &iv1_DATA
DATA(0x00, U32(0x510e527f))
DATA(0x04, U32(0x9b05688c))
DATA(0x08, U32(0x1f83d9ab))
DATA(0x0c, U32(0x5be0cd19))
return iv1_DATA
}
func rol16_DATA() Mem {
if rol16_DATA_ptr != nil {
return *rol16_DATA_ptr
}
rol16_DATA := GLOBL("rol16", NOPTR|RODATA)
rol16_DATA_ptr = &rol16_DATA
DATA(0x00, U64(0x0504070601000302))
DATA(0x08, U64(0x0D0C0F0E09080B0A))
return rol16_DATA
}
func rol8_DATA() Mem {
if rol8_DATA_ptr != nil {
return *rol8_DATA_ptr
}
rol8_DATA := GLOBL("rol8", NOPTR|RODATA)
rol8_DATA_ptr = &rol8_DATA
DATA(0x00, U64(0x0407060500030201))
DATA(0x08, U64(0x0C0F0E0D080B0A09))
return rol8_DATA
}
func counter_DATA() Mem {
if counter_DATA_ptr != nil {
return *counter_DATA_ptr
}
counter_DATA := GLOBL("counter", NOPTR|RODATA)
counter_DATA_ptr = &counter_DATA
DATA(0x00, U64(0x0000000000000040))
DATA(0x08, U64(0x0000000000000000))
return counter_DATA
}
-15
View File
@@ -1,15 +0,0 @@
module blake2s/_asm
go 1.23
require (
github.com/mmcloughlin/avo v0.6.0
golang.org/x/crypto v0.26.0
)
require (
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)
-12
View File
@@ -1,12 +0,0 @@
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+1 -9
View File
@@ -16,10 +16,9 @@
//
// BLAKE2X is a construction to compute hash values larger than 32 bytes. It
// can produce hash values between 0 and 65535 bytes.
package blake2s
package blake2s // import "golang.org/x/crypto/blake2s"
import (
"crypto"
"encoding/binary"
"errors"
"hash"
@@ -56,13 +55,6 @@ func Sum256(data []byte) [Size]byte {
// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
func init() {
crypto.RegisterHash(crypto.BLAKE2s_256, func() hash.Hash {
h, _ := New256(nil)
return h
})
}
// New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
// non-empty key. Note that a 128-bit digest is too small to be secure as a
// cryptographic hash and should only be used as a MAC, thus the key argument
+1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build 386 && gc && !purego
// +build 386,gc,!purego
package blake2s
+1 -1
View File
@@ -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 386 && gc && !purego
// +build 386,gc,!purego
#include "textflag.h"
+1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package blake2s
+420 -2161
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build (!amd64 && !386) || !gc || purego
// +build !amd64,!386 !gc purego
package blake2s
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.9
// +build go1.9
package blake2s
import (
"crypto"
"hash"
)
func init() {
newHash256 := func() hash.Hash {
h, _ := New256(nil)
return h
}
crypto.RegisterHash(crypto.BLAKE2s_256, newHash256)
}
+1 -1
View File
@@ -11,7 +11,7 @@
// Deprecated: any new system should use AES (from crypto/aes, if necessary in
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
// golang.org/x/crypto/chacha20poly1305).
package blowfish
package blowfish // import "golang.org/x/crypto/blowfish"
// The code is a port of Bruce Schneier's C implementation.
// See https://www.schneier.com/blowfish.html.
+2 -2
View File
@@ -23,7 +23,7 @@
// elliptic curve. This package is frozen, and not implemented in constant time.
// There is a more complete implementation at github.com/cloudflare/bn256, but
// note that it suffers from the same security issues of the underlying curve.
package bn256
package bn256 // import "golang.org/x/crypto/bn256"
import (
"crypto/rand"
@@ -162,7 +162,7 @@ type G2 struct {
p *twistPoint
}
// RandomG2 returns x and g₂ˣ where x is a random, non-zero number read from r.
// RandomG1 returns x and g₂ˣ where x is a random, non-zero number read from r.
func RandomG2(r io.Reader) (*big.Int, *G2, error) {
var k *big.Int
var err error
+5 -8
View File
@@ -11,12 +11,9 @@
// Deprecated: any new system should use AES (from crypto/aes, if necessary in
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
// golang.org/x/crypto/chacha20poly1305).
package cast5
package cast5 // import "golang.org/x/crypto/cast5"
import (
"errors"
"math/bits"
)
import "errors"
const BlockSize = 8
const KeySize = 16
@@ -244,19 +241,19 @@ func (c *Cipher) keySchedule(in []byte) {
// These are the three 'f' functions. See RFC 2144, section 2.2.
func f1(d, m uint32, r uint8) uint32 {
t := m + d
I := bits.RotateLeft32(t, int(r))
I := (t << r) | (t >> (32 - r))
return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff]
}
func f2(d, m uint32, r uint8) uint32 {
t := m ^ d
I := bits.RotateLeft32(t, int(r))
I := (t << r) | (t >> (32 - r))
return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff]
}
func f3(d, m uint32, r uint8) uint32 {
t := m - d
I := bits.RotateLeft32(t, int(r))
I := (t << r) | (t >> (32 - r))
return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff]
}
+2 -1
View File
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego
//go:build go1.11 && gc && !purego
// +build go1.11,gc,!purego
package chacha20
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego
// +build go1.11,gc,!purego
#include "textflag.h"
+2 -2
View File
@@ -12,7 +12,7 @@ import (
"errors"
"math/bits"
"golang.org/x/crypto/internal/alias"
"golang.org/x/crypto/internal/subtle"
)
const (
@@ -189,7 +189,7 @@ func (s *Cipher) XORKeyStream(dst, src []byte) {
panic("chacha20: output smaller than input")
}
dst = dst[:len(src)]
if alias.InexactOverlap(dst, src) {
if subtle.InexactOverlap(dst, src) {
panic("chacha20: invalid buffer overlap")
}
+2 -1
View File
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego
//go:build (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
package chacha20
+1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build gc && !purego
// +build gc,!purego
package chacha20
+59 -53
View File
@@ -19,7 +19,7 @@
// The differences in this and the original implementation are
// due to the calling conventions and initialization of constants.
//go:build gc && !purego
// +build gc,!purego
#include "textflag.h"
@@ -33,9 +33,6 @@
#define CONSTBASE R16
#define BLOCKS R17
// for VPERMXOR
#define MASK R18
DATA consts<>+0x00(SB)/8, $0x3320646e61707865
DATA consts<>+0x08(SB)/8, $0x6b20657479622d32
DATA consts<>+0x10(SB)/8, $0x0000000000000001
@@ -56,11 +53,7 @@ DATA consts<>+0x80(SB)/8, $0x6b2065746b206574
DATA consts<>+0x88(SB)/8, $0x6b2065746b206574
DATA consts<>+0x90(SB)/8, $0x0000000100000000
DATA consts<>+0x98(SB)/8, $0x0000000300000002
DATA consts<>+0xa0(SB)/8, $0x5566774411223300
DATA consts<>+0xa8(SB)/8, $0xddeeffcc99aabb88
DATA consts<>+0xb0(SB)/8, $0x6677445522330011
DATA consts<>+0xb8(SB)/8, $0xeeffccddaabb8899
GLOBL consts<>(SB), RODATA, $0xc0
GLOBL consts<>(SB), RODATA, $0xa0
//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
@@ -77,9 +70,6 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
MOVD $48, R10
MOVD $64, R11
SRD $6, LEN, BLOCKS
// for VPERMXOR
MOVD $consts<>+0xa0(SB), MASK
MOVD $16, R20
// V16
LXVW4X (CONSTBASE)(R0), VS48
ADD $80,CONSTBASE
@@ -97,10 +87,6 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
// V28
LXVW4X (CONSTBASE)(R11), VS60
// Load mask constants for VPERMXOR
LXVW4X (MASK)(R0), V20
LXVW4X (MASK)(R20), V21
// splat slot from V19 -> V26
VSPLTW $0, V19, V26
@@ -111,7 +97,7 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
MOVD $10, R14
MOVD R14, CTR
PCALIGN $16
loop_outer_vsx:
// V0, V1, V2, V3
LXVW4X (R0)(CONSTBASE), VS32
@@ -142,17 +128,22 @@ loop_outer_vsx:
VSPLTISW $12, V28
VSPLTISW $8, V29
VSPLTISW $7, V30
PCALIGN $16
loop_vsx:
VADDUWM V0, V4, V0
VADDUWM V1, V5, V1
VADDUWM V2, V6, V2
VADDUWM V3, V7, V3
VPERMXOR V12, V0, V21, V12
VPERMXOR V13, V1, V21, V13
VPERMXOR V14, V2, V21, V14
VPERMXOR V15, V3, V21, V15
VXOR V12, V0, V12
VXOR V13, V1, V13
VXOR V14, V2, V14
VXOR V15, V3, V15
VRLW V12, V27, V12
VRLW V13, V27, V13
VRLW V14, V27, V14
VRLW V15, V27, V15
VADDUWM V8, V12, V8
VADDUWM V9, V13, V9
@@ -174,10 +165,15 @@ loop_vsx:
VADDUWM V2, V6, V2
VADDUWM V3, V7, V3
VPERMXOR V12, V0, V20, V12
VPERMXOR V13, V1, V20, V13
VPERMXOR V14, V2, V20, V14
VPERMXOR V15, V3, V20, V15
VXOR V12, V0, V12
VXOR V13, V1, V13
VXOR V14, V2, V14
VXOR V15, V3, V15
VRLW V12, V29, V12
VRLW V13, V29, V13
VRLW V14, V29, V14
VRLW V15, V29, V15
VADDUWM V8, V12, V8
VADDUWM V9, V13, V9
@@ -199,10 +195,15 @@ loop_vsx:
VADDUWM V2, V7, V2
VADDUWM V3, V4, V3
VPERMXOR V15, V0, V21, V15
VPERMXOR V12, V1, V21, V12
VPERMXOR V13, V2, V21, V13
VPERMXOR V14, V3, V21, V14
VXOR V15, V0, V15
VXOR V12, V1, V12
VXOR V13, V2, V13
VXOR V14, V3, V14
VRLW V15, V27, V15
VRLW V12, V27, V12
VRLW V13, V27, V13
VRLW V14, V27, V14
VADDUWM V10, V15, V10
VADDUWM V11, V12, V11
@@ -224,10 +225,15 @@ loop_vsx:
VADDUWM V2, V7, V2
VADDUWM V3, V4, V3
VPERMXOR V15, V0, V20, V15
VPERMXOR V12, V1, V20, V12
VPERMXOR V13, V2, V20, V13
VPERMXOR V14, V3, V20, V14
VXOR V15, V0, V15
VXOR V12, V1, V12
VXOR V13, V2, V13
VXOR V14, V3, V14
VRLW V15, V29, V15
VRLW V12, V29, V12
VRLW V13, V29, V13
VRLW V14, V29, V14
VADDUWM V10, V15, V10
VADDUWM V11, V12, V11
@@ -243,48 +249,48 @@ loop_vsx:
VRLW V6, V30, V6
VRLW V7, V30, V7
VRLW V4, V30, V4
BDNZ loop_vsx
BC 16, LT, loop_vsx
VADDUWM V12, V26, V12
VMRGEW V0, V1, V27
VMRGEW V2, V3, V28
WORD $0x13600F8C // VMRGEW V0, V1, V27
WORD $0x13821F8C // VMRGEW V2, V3, V28
VMRGOW V0, V1, V0
VMRGOW V2, V3, V2
WORD $0x10000E8C // VMRGOW V0, V1, V0
WORD $0x10421E8C // VMRGOW V2, V3, V2
VMRGEW V4, V5, V29
VMRGEW V6, V7, V30
WORD $0x13A42F8C // VMRGEW V4, V5, V29
WORD $0x13C63F8C // VMRGEW V6, V7, V30
XXPERMDI VS32, VS34, $0, VS33
XXPERMDI VS32, VS34, $3, VS35
XXPERMDI VS59, VS60, $0, VS32
XXPERMDI VS59, VS60, $3, VS34
VMRGOW V4, V5, V4
VMRGOW V6, V7, V6
WORD $0x10842E8C // VMRGOW V4, V5, V4
WORD $0x10C63E8C // VMRGOW V6, V7, V6
VMRGEW V8, V9, V27
VMRGEW V10, V11, V28
WORD $0x13684F8C // VMRGEW V8, V9, V27
WORD $0x138A5F8C // VMRGEW V10, V11, V28
XXPERMDI VS36, VS38, $0, VS37
XXPERMDI VS36, VS38, $3, VS39
XXPERMDI VS61, VS62, $0, VS36
XXPERMDI VS61, VS62, $3, VS38
VMRGOW V8, V9, V8
VMRGOW V10, V11, V10
WORD $0x11084E8C // VMRGOW V8, V9, V8
WORD $0x114A5E8C // VMRGOW V10, V11, V10
VMRGEW V12, V13, V29
VMRGEW V14, V15, V30
WORD $0x13AC6F8C // VMRGEW V12, V13, V29
WORD $0x13CE7F8C // VMRGEW V14, V15, V30
XXPERMDI VS40, VS42, $0, VS41
XXPERMDI VS40, VS42, $3, VS43
XXPERMDI VS59, VS60, $0, VS40
XXPERMDI VS59, VS60, $3, VS42
VMRGOW V12, V13, V12
VMRGOW V14, V15, V14
WORD $0x118C6E8C // VMRGOW V12, V13, V12
WORD $0x11CE7E8C // VMRGOW V14, V15, V14
VSPLTISW $4, V27
VADDUWM V26, V27, V26
@@ -425,7 +431,7 @@ tail_vsx:
ADD $-1, R11, R12
ADD $-1, INP
ADD $-1, OUT
PCALIGN $16
looptail_vsx:
// Copying the result to OUT
// in bytes.
@@ -433,7 +439,7 @@ looptail_vsx:
MOVBZU 1(INP), TMP
XOR KEY, TMP, KEY
MOVBU KEY, 1(OUT)
BDNZ looptail_vsx
BC 16, LT, looptail_vsx
// Clear the stack values
STXVW4X VS48, (R11)(R0)
+1 -1
View File
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build gc && !purego
// +build gc,!purego
package chacha20
@@ -14,7 +15,6 @@ const bufSize = 256
// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only
// be called when the vector facility is available. Implementation in asm_s390x.s.
//
//go:noescape
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego
// +build gc,!purego
#include "go_asm.h"
#include "textflag.h"
File diff suppressed because it is too large Load Diff
-15
View File
@@ -1,15 +0,0 @@
module chacha20poly1305/_asm
go 1.23
require (
github.com/mmcloughlin/avo v0.6.0
golang.org/x/crypto v0.26.0
)
require (
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)
-12
View File
@@ -1,12 +0,0 @@
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+2 -6
View File
@@ -5,7 +5,7 @@
// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD and its
// extended nonce variant XChaCha20-Poly1305, as specified in RFC 8439 and
// draft-irtf-cfrg-xchacha-01.
package chacha20poly1305
package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305"
import (
"crypto/cipher"
@@ -26,10 +26,6 @@ const (
// NonceSizeX is the size of the nonce used with the XChaCha20-Poly1305
// variant of this AEAD, in bytes.
NonceSizeX = 24
// Overhead is the size of the Poly1305 authentication tag, and the
// difference between a ciphertext length and its plaintext.
Overhead = 16
)
type chacha20poly1305 struct {
@@ -51,7 +47,7 @@ func (c *chacha20poly1305) NonceSize() int {
}
func (c *chacha20poly1305) Overhead() int {
return Overhead
return 16
}
func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+4 -3
View File
@@ -3,13 +3,14 @@
// license that can be found in the LICENSE file.
//go:build gc && !purego
// +build gc,!purego
package chacha20poly1305
import (
"encoding/binary"
"golang.org/x/crypto/internal/alias"
"golang.org/x/crypto/internal/subtle"
"golang.org/x/sys/cpu"
)
@@ -55,7 +56,7 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []
setupState(&state, &c.key, nonce)
ret, out := sliceForAppend(dst, len(plaintext)+16)
if alias.InexactOverlap(out, plaintext) {
if subtle.InexactOverlap(out, plaintext) {
panic("chacha20poly1305: invalid buffer overlap")
}
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
@@ -72,7 +73,7 @@ func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) (
ciphertext = ciphertext[:len(ciphertext)-16]
ret, out := sliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
if subtle.InexactOverlap(out, ciphertext) {
panic("chacha20poly1305: invalid buffer overlap")
}
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -8,8 +8,8 @@ import (
"encoding/binary"
"golang.org/x/crypto/chacha20"
"golang.org/x/crypto/internal/alias"
"golang.org/x/crypto/internal/poly1305"
"golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/poly1305"
)
func writeWithPadding(p *poly1305.MAC, b []byte) {
@@ -30,7 +30,7 @@ func writeUint64(p *poly1305.MAC, n int) {
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
ciphertext, tag := out[:len(plaintext)], out[len(plaintext):]
if alias.InexactOverlap(out, plaintext) {
if subtle.InexactOverlap(out, plaintext) {
panic("chacha20poly1305: invalid buffer overlap")
}
@@ -66,7 +66,7 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
writeUint64(p, len(ciphertext))
ret, out := sliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
if subtle.InexactOverlap(out, ciphertext) {
panic("chacha20poly1305: invalid buffer overlap")
}
if !p.Verify(tag) {
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
//go:build !amd64 || !gc || purego
// +build !amd64 !gc purego
package chacha20poly1305
+1 -1
View File
@@ -35,7 +35,7 @@ func (*xchacha20poly1305) NonceSize() int {
}
func (*xchacha20poly1305) Overhead() int {
return Overhead
return 16
}
func (x *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+42 -58
View File
@@ -264,35 +264,36 @@ func (s *String) ReadASN1Boolean(out *bool) bool {
return true
}
var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
// not point to an integer, to a big.Int, or to a []byte it panics. Only
// positive and zero values can be decoded into []byte, and they are returned as
// big-endian binary values that share memory with s. Positive values will have
// no leading zeroes, and zero will be returned as a single zero byte.
// ReadASN1Integer reports whether the read was successful.
// not point to an integer or to a big.Int, it panics. It reports whether the
// read was successful.
func (s *String) ReadASN1Integer(out interface{}) bool {
switch out := out.(type) {
case *int, *int8, *int16, *int32, *int64:
if reflect.TypeOf(out).Kind() != reflect.Ptr {
panic("out is not a pointer")
}
switch reflect.ValueOf(out).Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var i int64
if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) {
return false
}
reflect.ValueOf(out).Elem().SetInt(i)
return true
case *uint, *uint8, *uint16, *uint32, *uint64:
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
var u uint64
if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) {
return false
}
reflect.ValueOf(out).Elem().SetUint(u)
return true
case *big.Int:
return s.readASN1BigInt(out)
case *[]byte:
return s.readASN1Bytes(out)
default:
panic("out does not point to an integer type")
case reflect.Struct:
if reflect.TypeOf(out).Elem() == bigIntType {
return s.readASN1BigInt(out.(*big.Int))
}
}
panic("out does not point to an integer type")
}
func checkASN1Integer(bytes []byte) bool {
@@ -332,21 +333,6 @@ func (s *String) readASN1BigInt(out *big.Int) bool {
return true
}
func (s *String) readASN1Bytes(out *[]byte) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
return false
}
if bytes[0]&0x80 == 0x80 {
return false
}
for len(bytes) > 1 && bytes[0] == 0 {
bytes = bytes[1:]
}
*out = bytes
return true
}
func (s *String) readASN1Int64(out *int64) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
@@ -431,14 +417,6 @@ func (s *String) readBase128Int(out *int) bool {
}
ret <<= 7
b := s.read(1)[0]
// ITU-T X.690, section 8.19.2:
// The subidentifier shall be encoded in the fewest possible octets,
// that is, the leading octet of the subidentifier shall not have the value 0x80.
if i == 0 && b == 0x80 {
return false
}
ret |= int(b & 0x7f)
if b&0x80 == 0 {
*out = ret
@@ -554,7 +532,7 @@ func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
return false
}
paddingBits := bytes[0]
paddingBits := uint8(bytes[0])
bytes = bytes[1:]
if paddingBits > 7 ||
len(bytes) == 0 && paddingBits != 0 ||
@@ -567,7 +545,7 @@ func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
return true
}
// ReadASN1BitStringAsBytes decodes an ASN.1 BIT STRING into out and advances. It is
// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. It is
// an error if the BIT STRING is not a whole number of bytes. It reports
// whether the read was successful.
func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool {
@@ -576,7 +554,7 @@ func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool {
return false
}
paddingBits := bytes[0]
paddingBits := uint8(bytes[0])
if paddingBits != 0 {
return false
}
@@ -676,27 +654,34 @@ func (s *String) SkipOptionalASN1(tag asn1.Tag) bool {
return s.ReadASN1(&unused, tag)
}
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER explicitly
// tagged with tag into out and advances. If no element with a matching tag is
// present, it writes defaultValue into out instead. Otherwise, it behaves like
// ReadASN1Integer.
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER
// explicitly tagged with tag into out and advances. If no element with a
// matching tag is present, it writes defaultValue into out instead. If out
// does not point to an integer or to a big.Int, it panics. It reports
// whether the read was successful.
func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool {
if reflect.TypeOf(out).Kind() != reflect.Ptr {
panic("out is not a pointer")
}
var present bool
var i String
if !s.ReadOptionalASN1(&i, &present, tag) {
return false
}
if !present {
switch out.(type) {
case *int, *int8, *int16, *int32, *int64,
*uint, *uint8, *uint16, *uint32, *uint64, *[]byte:
switch reflect.ValueOf(out).Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue))
case *big.Int:
if defaultValue, ok := defaultValue.(*big.Int); ok {
out.(*big.Int).Set(defaultValue)
} else {
case reflect.Struct:
if reflect.TypeOf(out).Elem() != bigIntType {
panic("invalid integer type")
}
if reflect.TypeOf(defaultValue).Kind() != reflect.Ptr ||
reflect.TypeOf(defaultValue).Elem() != bigIntType {
panic("out points to big.Int, but defaultValue does not")
}
out.(*big.Int).Set(defaultValue.(*big.Int))
default:
panic("invalid integer type")
}
@@ -733,14 +718,13 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
return true
}
// ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN
// explicitly tagged with tag into out and advances. If no element with a
// matching tag is present, it sets "out" to defaultValue instead. It reports
// whether the read was successful.
func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue bool) bool {
// ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or,
// if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue.
// It reports whether the operation was successful.
func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
var present bool
var child String
if !s.ReadOptionalASN1(&child, &present, tag) {
if !s.ReadOptionalASN1(&child, &present, asn1.BOOLEAN) {
return false
}
@@ -749,7 +733,7 @@ func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue b
return true
}
return child.ReadASN1Boolean(out)
return s.ReadASN1Boolean(out)
}
func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
+1 -1
View File
@@ -4,7 +4,7 @@
// Package asn1 contains supporting types for parsing and building ASN.1
// messages with the cryptobyte package.
package asn1
package asn1 // import "golang.org/x/crypto/cryptobyte/asn1"
// Tag represents an ASN.1 identifier octet, consisting of a tag number
// (indicating a type) and class (such as context-specific or constructed).
-49
View File
@@ -115,28 +115,6 @@ func TestReadASN1OptionalInteger(t *testing.T) {
}
}
const defaultBool = false
var optionalBoolTestData = []readASN1Test{
{"empty", []byte{}, 0xa0, true, false},
{"invalid", []byte{0xa1, 0x3, 0x1, 0x2, 0x7f}, 0xa1, false, defaultBool},
{"missing", []byte{0xa1, 0x3, 0x1, 0x1, 0x7f}, 0xa0, true, defaultBool},
{"present", []byte{0xa1, 0x3, 0x1, 0x1, 0xff}, 0xa1, true, true},
}
func TestReadASN1OptionalBoolean(t *testing.T) {
for _, test := range optionalBoolTestData {
t.Run(test.name, func(t *testing.T) {
in := String(test.in)
var out bool
ok := in.ReadOptionalASN1Boolean(&out, test.tag, defaultBool)
if ok != test.ok || ok && out != test.out.(bool) {
t.Errorf("in.ReadOptionalASN1Boolean() = %v, want %v; out = %v, want %v", ok, test.ok, out, test.out)
}
})
}
}
func TestReadASN1IntegerSigned(t *testing.T) {
testData64 := []struct {
in []byte
@@ -176,32 +154,6 @@ func TestReadASN1IntegerSigned(t *testing.T) {
}
})
// Repeat the same cases, reading into a []byte.
t.Run("bytes", func(t *testing.T) {
for i, test := range testData64 {
in := String(test.in)
var out []byte
ok := in.ReadASN1Integer(&out)
if test.out < 0 {
if ok {
t.Errorf("#%d: in.ReadASN1Integer(%d) = %v, want false", i, test.out, ok)
}
continue
}
if !ok {
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true", i, ok)
continue
}
n := new(big.Int).SetBytes(out).Int64()
if n != test.out {
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %x, want %d", i, ok, out, test.out)
}
if out[0] == 0 && len(out) > 1 {
t.Errorf("#%d: in.ReadASN1Integer() = %v; out = %x, has leading zeroes", i, ok, out)
}
}
})
// Repeat with the implicit-tagging functions
t.Run("WithTag", func(t *testing.T) {
for i, test := range testData64 {
@@ -298,7 +250,6 @@ func TestASN1ObjectIdentifier(t *testing.T) {
{[]byte{6, 7, 85, 0x02, 0x85, 0xc7, 0xcc, 0xfb, 0x01}, true, []int{2, 5, 2, 1492336001}},
{[]byte{6, 7, 0x55, 0x02, 0x87, 0xff, 0xff, 0xff, 0x7f}, true, []int{2, 5, 2, 2147483647}}, // 2**31-1
{[]byte{6, 7, 0x55, 0x02, 0x88, 0x80, 0x80, 0x80, 0x00}, false, []int{}}, // 2**31
{[]byte{6, 3, 85, 0x80, 0x02}, false, []int{}}, // leading 0x80 octet
}
for i, test := range testData {
+10 -23
View File
@@ -95,16 +95,6 @@ func (b *Builder) AddUint32(v uint32) {
b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
// AddUint48 appends a big-endian, 48-bit value to the byte string.
func (b *Builder) AddUint48(v uint64) {
b.add(byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
// AddUint64 appends a big-endian, 64-bit value to the byte string.
func (b *Builder) AddUint64(v uint64) {
b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
// AddBytes appends a sequence of bytes to the byte string.
func (b *Builder) AddBytes(v []byte) {
b.add(v...)
@@ -116,13 +106,13 @@ func (b *Builder) AddBytes(v []byte) {
// supplied to them. The child builder passed to the continuation can be used
// to build the content of the length-prefixed sequence. For example:
//
// parent := cryptobyte.NewBuilder()
// parent.AddUint8LengthPrefixed(func (child *Builder) {
// child.AddUint8(42)
// child.AddUint8LengthPrefixed(func (grandchild *Builder) {
// grandchild.AddUint8(5)
// })
// })
// parent := cryptobyte.NewBuilder()
// parent.AddUint8LengthPrefixed(func (child *Builder) {
// child.AddUint8(42)
// child.AddUint8LengthPrefixed(func (grandchild *Builder) {
// grandchild.AddUint8(5)
// })
// })
//
// It is an error to write more bytes to the child than allowed by the reserved
// length prefix. After the continuation returns, the child must be considered
@@ -308,9 +298,9 @@ func (b *Builder) add(bytes ...byte) {
b.result = append(b.result, bytes...)
}
// Unwrite rolls back non-negative n bytes written directly to the Builder.
// An attempt by a child builder passed to a continuation to unwrite bytes
// from its parent will panic.
// Unwrite rolls back n bytes written directly to the Builder. An attempt by a
// child builder passed to a continuation to unwrite bytes from its parent will
// panic.
func (b *Builder) Unwrite(n int) {
if b.err != nil {
return
@@ -322,9 +312,6 @@ func (b *Builder) Unwrite(n int) {
if length < 0 {
panic("cryptobyte: internal error")
}
if n < 0 {
panic("cryptobyte: attempted to unwrite negative number of bytes")
}
if n > length {
panic("cryptobyte: attempted to unwrite more than was written")
}
+2 -43
View File
@@ -141,7 +141,7 @@ func TestUint24(t *testing.T) {
var s String = b.BytesOrPanic()
var v uint32
if !s.ReadUint24(&v) {
t.Error("ReadUint24() = false, want true")
t.Error("ReadUint8() = false, want true")
}
if v != 0xfffefd {
t.Errorf("v = %d, want fffefd", v)
@@ -169,7 +169,7 @@ func TestUint32(t *testing.T) {
var s String = b.BytesOrPanic()
var v uint32
if !s.ReadUint32(&v) {
t.Error("ReadUint32() = false, want true")
t.Error("ReadUint8() = false, want true")
}
if v != 0xfffefdfc {
t.Errorf("v = %x, want fffefdfc", v)
@@ -179,47 +179,6 @@ func TestUint32(t *testing.T) {
}
}
func TestUint48(t *testing.T) {
var b Builder
var u uint64 = 0xfefcff3cfdfc
b.AddUint48(u)
if err := builderBytesEq(&b, 254, 252, 255, 60, 253, 252); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var v uint64
if !s.ReadUint48(&v) {
t.Error("ReadUint48() = false, want true")
}
if v != u {
t.Errorf("v = %x, want %x", v, u)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUint64(t *testing.T) {
var b Builder
b.AddUint64(0xf2fefefcff3cfdfc)
if err := builderBytesEq(&b, 242, 254, 254, 252, 255, 60, 253, 252); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var v uint64
if !s.ReadUint64(&v) {
t.Error("ReadUint64() = false, want true")
}
if v != 0xf2fefefcff3cfdfc {
t.Errorf("v = %x, want f2fefefcff3cfdfc", v)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUMultiple(t *testing.T) {
var b Builder
b.AddUint8(23)
+1 -23
View File
@@ -15,7 +15,7 @@
//
// See the documentation and examples for the Builder and String types to get
// started.
package cryptobyte
package cryptobyte // import "golang.org/x/crypto/cryptobyte"
// String represents a string of bytes. It provides methods for parsing
// fixed-length and length-prefixed values from it.
@@ -81,28 +81,6 @@ func (s *String) ReadUint32(out *uint32) bool {
return true
}
// ReadUint48 decodes a big-endian, 48-bit value into out and advances over it.
// It reports whether the read was successful.
func (s *String) ReadUint48(out *uint64) bool {
v := s.read(6)
if v == nil {
return false
}
*out = uint64(v[0])<<40 | uint64(v[1])<<32 | uint64(v[2])<<24 | uint64(v[3])<<16 | uint64(v[4])<<8 | uint64(v[5])
return true
}
// ReadUint64 decodes a big-endian, 64-bit value into out and advances over it.
// It reports whether the read was successful.
func (s *String) ReadUint64(out *uint64) bool {
v := s.read(8)
if v == nil {
return false
}
*out = uint64(v[0])<<56 | uint64(v[1])<<48 | uint64(v[2])<<40 | uint64(v[3])<<32 | uint64(v[4])<<24 | uint64(v[5])<<16 | uint64(v[6])<<8 | uint64(v[7])
return true
}
func (s *String) readUnsigned(out *uint32, length int) bool {
v := s.read(length)
if v == nil {
+35 -30
View File
@@ -5,12 +5,12 @@
// Package curve25519 provides an implementation of the X25519 function, which
// performs scalar multiplication on the elliptic curve known as Curve25519.
// See RFC 7748.
//
// This package is a wrapper for the X25519 implementation
// in the crypto/ecdh package.
package curve25519
package curve25519 // import "golang.org/x/crypto/curve25519"
import "crypto/ecdh"
import (
"crypto/subtle"
"fmt"
)
// ScalarMult sets dst to the product scalar * point.
//
@@ -18,13 +18,7 @@ import "crypto/ecdh"
// zeroes, irrespective of the scalar. Instead, use the X25519 function, which
// will return an error.
func ScalarMult(dst, scalar, point *[32]byte) {
if _, err := x25519(dst, scalar[:], point[:]); err != nil {
// The only error condition for x25519 when the inputs are 32 bytes long
// is if the output would have been the all-zero value.
for i := range dst {
dst[i] = 0
}
}
scalarMult(dst, scalar, point)
}
// ScalarBaseMult sets dst to the product scalar * base where base is the
@@ -33,12 +27,7 @@ func ScalarMult(dst, scalar, point *[32]byte) {
// It is recommended to use the X25519 function with Basepoint instead, as
// copying into fixed size arrays can lead to unexpected bugs.
func ScalarBaseMult(dst, scalar *[32]byte) {
curve := ecdh.X25519()
priv, err := curve.NewPrivateKey(scalar[:])
if err != nil {
panic("curve25519: internal error: scalarBaseMult was not 32 bytes")
}
copy(dst[:], priv.PublicKey().Bytes())
ScalarMult(dst, scalar, &basePoint)
}
const (
@@ -51,10 +40,21 @@ const (
// Basepoint is the canonical Curve25519 generator.
var Basepoint []byte
var basePoint = [32]byte{9}
var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
func init() { Basepoint = basePoint[:] }
func checkBasepoint() {
if subtle.ConstantTimeCompare(Basepoint, []byte{
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}) != 1 {
panic("curve25519: global Basepoint value was modified")
}
}
// X25519 returns the result of the scalar multiplication (scalar * point),
// according to RFC 7748, Section 5. scalar, point and the return value are
// slices of 32 bytes.
@@ -72,19 +72,24 @@ func X25519(scalar, point []byte) ([]byte, error) {
}
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
curve := ecdh.X25519()
pub, err := curve.NewPublicKey(point)
if err != nil {
return nil, err
var in [32]byte
if l := len(scalar); l != 32 {
return nil, fmt.Errorf("bad scalar length: %d, expected %d", l, 32)
}
priv, err := curve.NewPrivateKey(scalar)
if err != nil {
return nil, err
if l := len(point); l != 32 {
return nil, fmt.Errorf("bad point length: %d, expected %d", l, 32)
}
out, err := priv.ECDH(pub)
if err != nil {
return nil, err
copy(in[:], scalar)
if &point[0] == &Basepoint[0] {
checkBasepoint()
ScalarBaseMult(dst, &in)
} else {
var base, zero [32]byte
copy(base[:], point)
ScalarMult(dst, &in, &base)
if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 {
return nil, fmt.Errorf("bad input point: low order point")
}
}
copy(dst[:], out)
return dst[:], nil
}
+241
View File
@@ -0,0 +1,241 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package curve25519
// These functions are implemented in the .s files. The names of the functions
// in the rest of the file are also taken from the SUPERCOP sources to help
// people following along.
//go:noescape
func cswap(inout *[5]uint64, v uint64)
//go:noescape
func ladderstep(inout *[5][5]uint64)
//go:noescape
func freeze(inout *[5]uint64)
//go:noescape
func mul(dest, a, b *[5]uint64)
//go:noescape
func square(out, in *[5]uint64)
// mladder uses a Montgomery ladder to calculate (xr/zr) *= s.
func mladder(xr, zr *[5]uint64, s *[32]byte) {
var work [5][5]uint64
work[0] = *xr
setint(&work[1], 1)
setint(&work[2], 0)
work[3] = *xr
setint(&work[4], 1)
j := uint(6)
var prevbit byte
for i := 31; i >= 0; i-- {
for j < 8 {
bit := ((*s)[i] >> j) & 1
swap := bit ^ prevbit
prevbit = bit
cswap(&work[1], uint64(swap))
ladderstep(&work)
j--
}
j = 7
}
*xr = work[1]
*zr = work[2]
}
func scalarMult(out, in, base *[32]byte) {
var e [32]byte
copy(e[:], (*in)[:])
e[0] &= 248
e[31] &= 127
e[31] |= 64
var t, z [5]uint64
unpack(&t, base)
mladder(&t, &z, &e)
invert(&z, &z)
mul(&t, &t, &z)
pack(out, &t)
}
func setint(r *[5]uint64, v uint64) {
r[0] = v
r[1] = 0
r[2] = 0
r[3] = 0
r[4] = 0
}
// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian
// order.
func unpack(r *[5]uint64, x *[32]byte) {
r[0] = uint64(x[0]) |
uint64(x[1])<<8 |
uint64(x[2])<<16 |
uint64(x[3])<<24 |
uint64(x[4])<<32 |
uint64(x[5])<<40 |
uint64(x[6]&7)<<48
r[1] = uint64(x[6])>>3 |
uint64(x[7])<<5 |
uint64(x[8])<<13 |
uint64(x[9])<<21 |
uint64(x[10])<<29 |
uint64(x[11])<<37 |
uint64(x[12]&63)<<45
r[2] = uint64(x[12])>>6 |
uint64(x[13])<<2 |
uint64(x[14])<<10 |
uint64(x[15])<<18 |
uint64(x[16])<<26 |
uint64(x[17])<<34 |
uint64(x[18])<<42 |
uint64(x[19]&1)<<50
r[3] = uint64(x[19])>>1 |
uint64(x[20])<<7 |
uint64(x[21])<<15 |
uint64(x[22])<<23 |
uint64(x[23])<<31 |
uint64(x[24])<<39 |
uint64(x[25]&15)<<47
r[4] = uint64(x[25])>>4 |
uint64(x[26])<<4 |
uint64(x[27])<<12 |
uint64(x[28])<<20 |
uint64(x[29])<<28 |
uint64(x[30])<<36 |
uint64(x[31]&127)<<44
}
// pack sets out = x where out is the usual, little-endian form of the 5,
// 51-bit limbs in x.
func pack(out *[32]byte, x *[5]uint64) {
t := *x
freeze(&t)
out[0] = byte(t[0])
out[1] = byte(t[0] >> 8)
out[2] = byte(t[0] >> 16)
out[3] = byte(t[0] >> 24)
out[4] = byte(t[0] >> 32)
out[5] = byte(t[0] >> 40)
out[6] = byte(t[0] >> 48)
out[6] ^= byte(t[1]<<3) & 0xf8
out[7] = byte(t[1] >> 5)
out[8] = byte(t[1] >> 13)
out[9] = byte(t[1] >> 21)
out[10] = byte(t[1] >> 29)
out[11] = byte(t[1] >> 37)
out[12] = byte(t[1] >> 45)
out[12] ^= byte(t[2]<<6) & 0xc0
out[13] = byte(t[2] >> 2)
out[14] = byte(t[2] >> 10)
out[15] = byte(t[2] >> 18)
out[16] = byte(t[2] >> 26)
out[17] = byte(t[2] >> 34)
out[18] = byte(t[2] >> 42)
out[19] = byte(t[2] >> 50)
out[19] ^= byte(t[3]<<1) & 0xfe
out[20] = byte(t[3] >> 7)
out[21] = byte(t[3] >> 15)
out[22] = byte(t[3] >> 23)
out[23] = byte(t[3] >> 31)
out[24] = byte(t[3] >> 39)
out[25] = byte(t[3] >> 47)
out[25] ^= byte(t[4]<<4) & 0xf0
out[26] = byte(t[4] >> 4)
out[27] = byte(t[4] >> 12)
out[28] = byte(t[4] >> 20)
out[29] = byte(t[4] >> 28)
out[30] = byte(t[4] >> 36)
out[31] = byte(t[4] >> 44)
}
// invert calculates r = x^-1 mod p using Fermat's little theorem.
func invert(r *[5]uint64, x *[5]uint64) {
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64
square(&z2, x) /* 2 */
square(&t, &z2) /* 4 */
square(&t, &t) /* 8 */
mul(&z9, &t, x) /* 9 */
mul(&z11, &z9, &z2) /* 11 */
square(&t, &z11) /* 22 */
mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */
square(&t, &z2_5_0) /* 2^6 - 2^1 */
for i := 1; i < 5; i++ { /* 2^20 - 2^10 */
square(&t, &t)
}
mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */
square(&t, &z2_10_0) /* 2^11 - 2^1 */
for i := 1; i < 10; i++ { /* 2^20 - 2^10 */
square(&t, &t)
}
mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */
square(&t, &z2_20_0) /* 2^21 - 2^1 */
for i := 1; i < 20; i++ { /* 2^40 - 2^20 */
square(&t, &t)
}
mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */
square(&t, &t) /* 2^41 - 2^1 */
for i := 1; i < 10; i++ { /* 2^50 - 2^10 */
square(&t, &t)
}
mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */
square(&t, &z2_50_0) /* 2^51 - 2^1 */
for i := 1; i < 50; i++ { /* 2^100 - 2^50 */
square(&t, &t)
}
mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */
square(&t, &z2_100_0) /* 2^101 - 2^1 */
for i := 1; i < 100; i++ { /* 2^200 - 2^100 */
square(&t, &t)
}
mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */
square(&t, &t) /* 2^201 - 2^1 */
for i := 1; i < 50; i++ { /* 2^250 - 2^50 */
square(&t, &t)
}
mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */
square(&t, &t) /* 2^251 - 2^1 */
square(&t, &t) /* 2^252 - 2^2 */
square(&t, &t) /* 2^253 - 2^3 */
square(&t, &t) /* 2^254 - 2^4 */
square(&t, &t) /* 2^255 - 2^5 */
mul(r, &t, &z11) /* 2^255 - 21 */
}
File diff suppressed because it is too large Load Diff
+828
View File
@@ -0,0 +1,828 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package curve25519
import "encoding/binary"
// This code is a port of the public domain, "ref10" implementation of
// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
// fieldElement represents an element of the field GF(2^255 - 19). An element
// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
// context.
type fieldElement [10]int32
func feZero(fe *fieldElement) {
for i := range fe {
fe[i] = 0
}
}
func feOne(fe *fieldElement) {
feZero(fe)
fe[0] = 1
}
func feAdd(dst, a, b *fieldElement) {
for i := range dst {
dst[i] = a[i] + b[i]
}
}
func feSub(dst, a, b *fieldElement) {
for i := range dst {
dst[i] = a[i] - b[i]
}
}
func feCopy(dst, src *fieldElement) {
for i := range dst {
dst[i] = src[i]
}
}
// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0.
//
// Preconditions: b in {0,1}.
func feCSwap(f, g *fieldElement, b int32) {
b = -b
for i := range f {
t := b & (f[i] ^ g[i])
f[i] ^= t
g[i] ^= t
}
}
// load3 reads a 24-bit, little-endian value from in.
func load3(in []byte) int64 {
var r int64
r = int64(in[0])
r |= int64(in[1]) << 8
r |= int64(in[2]) << 16
return r
}
// load4 reads a 32-bit, little-endian value from in.
func load4(in []byte) int64 {
return int64(binary.LittleEndian.Uint32(in))
}
func feFromBytes(dst *fieldElement, src *[32]byte) {
h0 := load4(src[:])
h1 := load3(src[4:]) << 6
h2 := load3(src[7:]) << 5
h3 := load3(src[10:]) << 3
h4 := load3(src[13:]) << 2
h5 := load4(src[16:])
h6 := load3(src[20:]) << 7
h7 := load3(src[23:]) << 5
h8 := load3(src[26:]) << 4
h9 := (load3(src[29:]) & 0x7fffff) << 2
var carry [10]int64
carry[9] = (h9 + 1<<24) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
carry[1] = (h1 + 1<<24) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[3] = (h3 + 1<<24) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[5] = (h5 + 1<<24) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
carry[7] = (h7 + 1<<24) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
carry[0] = (h0 + 1<<25) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[2] = (h2 + 1<<25) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[4] = (h4 + 1<<25) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[6] = (h6 + 1<<25) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
carry[8] = (h8 + 1<<25) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
dst[0] = int32(h0)
dst[1] = int32(h1)
dst[2] = int32(h2)
dst[3] = int32(h3)
dst[4] = int32(h4)
dst[5] = int32(h5)
dst[6] = int32(h6)
dst[7] = int32(h7)
dst[8] = int32(h8)
dst[9] = int32(h9)
}
// feToBytes marshals h to s.
// Preconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
//
// Write p=2^255-19; q=floor(h/p).
// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
//
// Proof:
// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
//
// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
// Then 0<y<1.
//
// Write r=h-pq.
// Have 0<=r<=p-1=2^255-20.
// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
//
// Write x=r+19(2^-255)r+y.
// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
//
// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
func feToBytes(s *[32]byte, h *fieldElement) {
var carry [10]int32
q := (19*h[9] + (1 << 24)) >> 25
q = (h[0] + q) >> 26
q = (h[1] + q) >> 25
q = (h[2] + q) >> 26
q = (h[3] + q) >> 25
q = (h[4] + q) >> 26
q = (h[5] + q) >> 25
q = (h[6] + q) >> 26
q = (h[7] + q) >> 25
q = (h[8] + q) >> 26
q = (h[9] + q) >> 25
// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
h[0] += 19 * q
// Goal: Output h-2^255 q, which is between 0 and 2^255-20.
carry[0] = h[0] >> 26
h[1] += carry[0]
h[0] -= carry[0] << 26
carry[1] = h[1] >> 25
h[2] += carry[1]
h[1] -= carry[1] << 25
carry[2] = h[2] >> 26
h[3] += carry[2]
h[2] -= carry[2] << 26
carry[3] = h[3] >> 25
h[4] += carry[3]
h[3] -= carry[3] << 25
carry[4] = h[4] >> 26
h[5] += carry[4]
h[4] -= carry[4] << 26
carry[5] = h[5] >> 25
h[6] += carry[5]
h[5] -= carry[5] << 25
carry[6] = h[6] >> 26
h[7] += carry[6]
h[6] -= carry[6] << 26
carry[7] = h[7] >> 25
h[8] += carry[7]
h[7] -= carry[7] << 25
carry[8] = h[8] >> 26
h[9] += carry[8]
h[8] -= carry[8] << 26
carry[9] = h[9] >> 25
h[9] -= carry[9] << 25
// h10 = carry9
// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
// Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
// evidently 2^255 h10-2^255 q = 0.
// Goal: Output h[0]+...+2^230 h[9].
s[0] = byte(h[0] >> 0)
s[1] = byte(h[0] >> 8)
s[2] = byte(h[0] >> 16)
s[3] = byte((h[0] >> 24) | (h[1] << 2))
s[4] = byte(h[1] >> 6)
s[5] = byte(h[1] >> 14)
s[6] = byte((h[1] >> 22) | (h[2] << 3))
s[7] = byte(h[2] >> 5)
s[8] = byte(h[2] >> 13)
s[9] = byte((h[2] >> 21) | (h[3] << 5))
s[10] = byte(h[3] >> 3)
s[11] = byte(h[3] >> 11)
s[12] = byte((h[3] >> 19) | (h[4] << 6))
s[13] = byte(h[4] >> 2)
s[14] = byte(h[4] >> 10)
s[15] = byte(h[4] >> 18)
s[16] = byte(h[5] >> 0)
s[17] = byte(h[5] >> 8)
s[18] = byte(h[5] >> 16)
s[19] = byte((h[5] >> 24) | (h[6] << 1))
s[20] = byte(h[6] >> 7)
s[21] = byte(h[6] >> 15)
s[22] = byte((h[6] >> 23) | (h[7] << 3))
s[23] = byte(h[7] >> 5)
s[24] = byte(h[7] >> 13)
s[25] = byte((h[7] >> 21) | (h[8] << 4))
s[26] = byte(h[8] >> 4)
s[27] = byte(h[8] >> 12)
s[28] = byte((h[8] >> 20) | (h[9] << 6))
s[29] = byte(h[9] >> 2)
s[30] = byte(h[9] >> 10)
s[31] = byte(h[9] >> 18)
}
// feMul calculates h = f * g
// Can overlap h with f or g.
//
// Preconditions:
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
//
// Postconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
//
// Notes on implementation strategy:
//
// Using schoolbook multiplication.
// Karatsuba would save a little in some cost models.
//
// Most multiplications by 2 and 19 are 32-bit precomputations;
// cheaper than 64-bit postcomputations.
//
// There is one remaining multiplication by 19 in the carry chain;
// one *19 precomputation can be merged into this,
// but the resulting data flow is considerably less clean.
//
// There are 12 carries below.
// 10 of them are 2-way parallelizable and vectorizable.
// Can get away with 11 carries, but then data flow is much deeper.
//
// With tighter constraints on inputs can squeeze carries into int32.
func feMul(h, f, g *fieldElement) {
f0 := f[0]
f1 := f[1]
f2 := f[2]
f3 := f[3]
f4 := f[4]
f5 := f[5]
f6 := f[6]
f7 := f[7]
f8 := f[8]
f9 := f[9]
g0 := g[0]
g1 := g[1]
g2 := g[2]
g3 := g[3]
g4 := g[4]
g5 := g[5]
g6 := g[6]
g7 := g[7]
g8 := g[8]
g9 := g[9]
g1_19 := 19 * g1 // 1.4*2^29
g2_19 := 19 * g2 // 1.4*2^30; still ok
g3_19 := 19 * g3
g4_19 := 19 * g4
g5_19 := 19 * g5
g6_19 := 19 * g6
g7_19 := 19 * g7
g8_19 := 19 * g8
g9_19 := 19 * g9
f1_2 := 2 * f1
f3_2 := 2 * f3
f5_2 := 2 * f5
f7_2 := 2 * f7
f9_2 := 2 * f9
f0g0 := int64(f0) * int64(g0)
f0g1 := int64(f0) * int64(g1)
f0g2 := int64(f0) * int64(g2)
f0g3 := int64(f0) * int64(g3)
f0g4 := int64(f0) * int64(g4)
f0g5 := int64(f0) * int64(g5)
f0g6 := int64(f0) * int64(g6)
f0g7 := int64(f0) * int64(g7)
f0g8 := int64(f0) * int64(g8)
f0g9 := int64(f0) * int64(g9)
f1g0 := int64(f1) * int64(g0)
f1g1_2 := int64(f1_2) * int64(g1)
f1g2 := int64(f1) * int64(g2)
f1g3_2 := int64(f1_2) * int64(g3)
f1g4 := int64(f1) * int64(g4)
f1g5_2 := int64(f1_2) * int64(g5)
f1g6 := int64(f1) * int64(g6)
f1g7_2 := int64(f1_2) * int64(g7)
f1g8 := int64(f1) * int64(g8)
f1g9_38 := int64(f1_2) * int64(g9_19)
f2g0 := int64(f2) * int64(g0)
f2g1 := int64(f2) * int64(g1)
f2g2 := int64(f2) * int64(g2)
f2g3 := int64(f2) * int64(g3)
f2g4 := int64(f2) * int64(g4)
f2g5 := int64(f2) * int64(g5)
f2g6 := int64(f2) * int64(g6)
f2g7 := int64(f2) * int64(g7)
f2g8_19 := int64(f2) * int64(g8_19)
f2g9_19 := int64(f2) * int64(g9_19)
f3g0 := int64(f3) * int64(g0)
f3g1_2 := int64(f3_2) * int64(g1)
f3g2 := int64(f3) * int64(g2)
f3g3_2 := int64(f3_2) * int64(g3)
f3g4 := int64(f3) * int64(g4)
f3g5_2 := int64(f3_2) * int64(g5)
f3g6 := int64(f3) * int64(g6)
f3g7_38 := int64(f3_2) * int64(g7_19)
f3g8_19 := int64(f3) * int64(g8_19)
f3g9_38 := int64(f3_2) * int64(g9_19)
f4g0 := int64(f4) * int64(g0)
f4g1 := int64(f4) * int64(g1)
f4g2 := int64(f4) * int64(g2)
f4g3 := int64(f4) * int64(g3)
f4g4 := int64(f4) * int64(g4)
f4g5 := int64(f4) * int64(g5)
f4g6_19 := int64(f4) * int64(g6_19)
f4g7_19 := int64(f4) * int64(g7_19)
f4g8_19 := int64(f4) * int64(g8_19)
f4g9_19 := int64(f4) * int64(g9_19)
f5g0 := int64(f5) * int64(g0)
f5g1_2 := int64(f5_2) * int64(g1)
f5g2 := int64(f5) * int64(g2)
f5g3_2 := int64(f5_2) * int64(g3)
f5g4 := int64(f5) * int64(g4)
f5g5_38 := int64(f5_2) * int64(g5_19)
f5g6_19 := int64(f5) * int64(g6_19)
f5g7_38 := int64(f5_2) * int64(g7_19)
f5g8_19 := int64(f5) * int64(g8_19)
f5g9_38 := int64(f5_2) * int64(g9_19)
f6g0 := int64(f6) * int64(g0)
f6g1 := int64(f6) * int64(g1)
f6g2 := int64(f6) * int64(g2)
f6g3 := int64(f6) * int64(g3)
f6g4_19 := int64(f6) * int64(g4_19)
f6g5_19 := int64(f6) * int64(g5_19)
f6g6_19 := int64(f6) * int64(g6_19)
f6g7_19 := int64(f6) * int64(g7_19)
f6g8_19 := int64(f6) * int64(g8_19)
f6g9_19 := int64(f6) * int64(g9_19)
f7g0 := int64(f7) * int64(g0)
f7g1_2 := int64(f7_2) * int64(g1)
f7g2 := int64(f7) * int64(g2)
f7g3_38 := int64(f7_2) * int64(g3_19)
f7g4_19 := int64(f7) * int64(g4_19)
f7g5_38 := int64(f7_2) * int64(g5_19)
f7g6_19 := int64(f7) * int64(g6_19)
f7g7_38 := int64(f7_2) * int64(g7_19)
f7g8_19 := int64(f7) * int64(g8_19)
f7g9_38 := int64(f7_2) * int64(g9_19)
f8g0 := int64(f8) * int64(g0)
f8g1 := int64(f8) * int64(g1)
f8g2_19 := int64(f8) * int64(g2_19)
f8g3_19 := int64(f8) * int64(g3_19)
f8g4_19 := int64(f8) * int64(g4_19)
f8g5_19 := int64(f8) * int64(g5_19)
f8g6_19 := int64(f8) * int64(g6_19)
f8g7_19 := int64(f8) * int64(g7_19)
f8g8_19 := int64(f8) * int64(g8_19)
f8g9_19 := int64(f8) * int64(g9_19)
f9g0 := int64(f9) * int64(g0)
f9g1_38 := int64(f9_2) * int64(g1_19)
f9g2_19 := int64(f9) * int64(g2_19)
f9g3_38 := int64(f9_2) * int64(g3_19)
f9g4_19 := int64(f9) * int64(g4_19)
f9g5_38 := int64(f9_2) * int64(g5_19)
f9g6_19 := int64(f9) * int64(g6_19)
f9g7_38 := int64(f9_2) * int64(g7_19)
f9g8_19 := int64(f9) * int64(g8_19)
f9g9_38 := int64(f9_2) * int64(g9_19)
h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38
h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19
h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38
h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19
h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38
h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19
h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38
h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19
h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38
h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0
var carry [10]int64
// |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
// i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
// |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
// i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
// |h0| <= 2^25
// |h4| <= 2^25
// |h1| <= 1.51*2^58
// |h5| <= 1.51*2^58
carry[1] = (h1 + (1 << 24)) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[5] = (h5 + (1 << 24)) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
// |h1| <= 2^24; from now on fits into int32
// |h5| <= 2^24; from now on fits into int32
// |h2| <= 1.21*2^59
// |h6| <= 1.21*2^59
carry[2] = (h2 + (1 << 25)) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[6] = (h6 + (1 << 25)) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
// |h2| <= 2^25; from now on fits into int32 unchanged
// |h6| <= 2^25; from now on fits into int32 unchanged
// |h3| <= 1.51*2^58
// |h7| <= 1.51*2^58
carry[3] = (h3 + (1 << 24)) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[7] = (h7 + (1 << 24)) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
// |h3| <= 2^24; from now on fits into int32 unchanged
// |h7| <= 2^24; from now on fits into int32 unchanged
// |h4| <= 1.52*2^33
// |h8| <= 1.52*2^33
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[8] = (h8 + (1 << 25)) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
// |h4| <= 2^25; from now on fits into int32 unchanged
// |h8| <= 2^25; from now on fits into int32 unchanged
// |h5| <= 1.01*2^24
// |h9| <= 1.51*2^58
carry[9] = (h9 + (1 << 24)) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
// |h9| <= 2^24; from now on fits into int32 unchanged
// |h0| <= 1.8*2^37
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
// |h0| <= 2^25; from now on fits into int32 unchanged
// |h1| <= 1.01*2^24
h[0] = int32(h0)
h[1] = int32(h1)
h[2] = int32(h2)
h[3] = int32(h3)
h[4] = int32(h4)
h[5] = int32(h5)
h[6] = int32(h6)
h[7] = int32(h7)
h[8] = int32(h8)
h[9] = int32(h9)
}
// feSquare calculates h = f*f. Can overlap h with f.
//
// Preconditions:
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
//
// Postconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
func feSquare(h, f *fieldElement) {
f0 := f[0]
f1 := f[1]
f2 := f[2]
f3 := f[3]
f4 := f[4]
f5 := f[5]
f6 := f[6]
f7 := f[7]
f8 := f[8]
f9 := f[9]
f0_2 := 2 * f0
f1_2 := 2 * f1
f2_2 := 2 * f2
f3_2 := 2 * f3
f4_2 := 2 * f4
f5_2 := 2 * f5
f6_2 := 2 * f6
f7_2 := 2 * f7
f5_38 := 38 * f5 // 1.31*2^30
f6_19 := 19 * f6 // 1.31*2^30
f7_38 := 38 * f7 // 1.31*2^30
f8_19 := 19 * f8 // 1.31*2^30
f9_38 := 38 * f9 // 1.31*2^30
f0f0 := int64(f0) * int64(f0)
f0f1_2 := int64(f0_2) * int64(f1)
f0f2_2 := int64(f0_2) * int64(f2)
f0f3_2 := int64(f0_2) * int64(f3)
f0f4_2 := int64(f0_2) * int64(f4)
f0f5_2 := int64(f0_2) * int64(f5)
f0f6_2 := int64(f0_2) * int64(f6)
f0f7_2 := int64(f0_2) * int64(f7)
f0f8_2 := int64(f0_2) * int64(f8)
f0f9_2 := int64(f0_2) * int64(f9)
f1f1_2 := int64(f1_2) * int64(f1)
f1f2_2 := int64(f1_2) * int64(f2)
f1f3_4 := int64(f1_2) * int64(f3_2)
f1f4_2 := int64(f1_2) * int64(f4)
f1f5_4 := int64(f1_2) * int64(f5_2)
f1f6_2 := int64(f1_2) * int64(f6)
f1f7_4 := int64(f1_2) * int64(f7_2)
f1f8_2 := int64(f1_2) * int64(f8)
f1f9_76 := int64(f1_2) * int64(f9_38)
f2f2 := int64(f2) * int64(f2)
f2f3_2 := int64(f2_2) * int64(f3)
f2f4_2 := int64(f2_2) * int64(f4)
f2f5_2 := int64(f2_2) * int64(f5)
f2f6_2 := int64(f2_2) * int64(f6)
f2f7_2 := int64(f2_2) * int64(f7)
f2f8_38 := int64(f2_2) * int64(f8_19)
f2f9_38 := int64(f2) * int64(f9_38)
f3f3_2 := int64(f3_2) * int64(f3)
f3f4_2 := int64(f3_2) * int64(f4)
f3f5_4 := int64(f3_2) * int64(f5_2)
f3f6_2 := int64(f3_2) * int64(f6)
f3f7_76 := int64(f3_2) * int64(f7_38)
f3f8_38 := int64(f3_2) * int64(f8_19)
f3f9_76 := int64(f3_2) * int64(f9_38)
f4f4 := int64(f4) * int64(f4)
f4f5_2 := int64(f4_2) * int64(f5)
f4f6_38 := int64(f4_2) * int64(f6_19)
f4f7_38 := int64(f4) * int64(f7_38)
f4f8_38 := int64(f4_2) * int64(f8_19)
f4f9_38 := int64(f4) * int64(f9_38)
f5f5_38 := int64(f5) * int64(f5_38)
f5f6_38 := int64(f5_2) * int64(f6_19)
f5f7_76 := int64(f5_2) * int64(f7_38)
f5f8_38 := int64(f5_2) * int64(f8_19)
f5f9_76 := int64(f5_2) * int64(f9_38)
f6f6_19 := int64(f6) * int64(f6_19)
f6f7_38 := int64(f6) * int64(f7_38)
f6f8_38 := int64(f6_2) * int64(f8_19)
f6f9_38 := int64(f6) * int64(f9_38)
f7f7_38 := int64(f7) * int64(f7_38)
f7f8_38 := int64(f7_2) * int64(f8_19)
f7f9_76 := int64(f7_2) * int64(f9_38)
f8f8_19 := int64(f8) * int64(f8_19)
f8f9_38 := int64(f8) * int64(f9_38)
f9f9_38 := int64(f9) * int64(f9_38)
h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38
h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38
h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19
h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38
h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38
h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38
h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19
h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38
h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38
h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2
var carry [10]int64
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[1] = (h1 + (1 << 24)) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[5] = (h5 + (1 << 24)) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
carry[2] = (h2 + (1 << 25)) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[6] = (h6 + (1 << 25)) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
carry[3] = (h3 + (1 << 24)) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[7] = (h7 + (1 << 24)) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[8] = (h8 + (1 << 25)) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
carry[9] = (h9 + (1 << 24)) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
h[0] = int32(h0)
h[1] = int32(h1)
h[2] = int32(h2)
h[3] = int32(h3)
h[4] = int32(h4)
h[5] = int32(h5)
h[6] = int32(h6)
h[7] = int32(h7)
h[8] = int32(h8)
h[9] = int32(h9)
}
// feMul121666 calculates h = f * 121666. Can overlap h with f.
//
// Preconditions:
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
//
// Postconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
func feMul121666(h, f *fieldElement) {
h0 := int64(f[0]) * 121666
h1 := int64(f[1]) * 121666
h2 := int64(f[2]) * 121666
h3 := int64(f[3]) * 121666
h4 := int64(f[4]) * 121666
h5 := int64(f[5]) * 121666
h6 := int64(f[6]) * 121666
h7 := int64(f[7]) * 121666
h8 := int64(f[8]) * 121666
h9 := int64(f[9]) * 121666
var carry [10]int64
carry[9] = (h9 + (1 << 24)) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
carry[1] = (h1 + (1 << 24)) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[3] = (h3 + (1 << 24)) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[5] = (h5 + (1 << 24)) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
carry[7] = (h7 + (1 << 24)) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[2] = (h2 + (1 << 25)) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[6] = (h6 + (1 << 25)) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
carry[8] = (h8 + (1 << 25)) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
h[0] = int32(h0)
h[1] = int32(h1)
h[2] = int32(h2)
h[3] = int32(h3)
h[4] = int32(h4)
h[5] = int32(h5)
h[6] = int32(h6)
h[7] = int32(h7)
h[8] = int32(h8)
h[9] = int32(h9)
}
// feInvert sets out = z^-1.
func feInvert(out, z *fieldElement) {
var t0, t1, t2, t3 fieldElement
var i int
feSquare(&t0, z)
for i = 1; i < 1; i++ {
feSquare(&t0, &t0)
}
feSquare(&t1, &t0)
for i = 1; i < 2; i++ {
feSquare(&t1, &t1)
}
feMul(&t1, z, &t1)
feMul(&t0, &t0, &t1)
feSquare(&t2, &t0)
for i = 1; i < 1; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t1, &t2)
feSquare(&t2, &t1)
for i = 1; i < 5; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t2, &t1)
feSquare(&t2, &t1)
for i = 1; i < 10; i++ {
feSquare(&t2, &t2)
}
feMul(&t2, &t2, &t1)
feSquare(&t3, &t2)
for i = 1; i < 20; i++ {
feSquare(&t3, &t3)
}
feMul(&t2, &t3, &t2)
feSquare(&t2, &t2)
for i = 1; i < 10; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t2, &t1)
feSquare(&t2, &t1)
for i = 1; i < 50; i++ {
feSquare(&t2, &t2)
}
feMul(&t2, &t2, &t1)
feSquare(&t3, &t2)
for i = 1; i < 100; i++ {
feSquare(&t3, &t3)
}
feMul(&t2, &t3, &t2)
feSquare(&t2, &t2)
for i = 1; i < 50; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t2, &t1)
feSquare(&t1, &t1)
for i = 1; i < 5; i++ {
feSquare(&t1, &t1)
}
feMul(out, &t1, &t0)
}
func scalarMultGeneric(out, in, base *[32]byte) {
var e [32]byte
copy(e[:], in[:])
e[0] &= 248
e[31] &= 127
e[31] |= 64
var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement
feFromBytes(&x1, base)
feOne(&x2)
feCopy(&x3, &x1)
feOne(&z3)
swap := int32(0)
for pos := 254; pos >= 0; pos-- {
b := e[pos/8] >> uint(pos&7)
b &= 1
swap ^= int32(b)
feCSwap(&x2, &x3, swap)
feCSwap(&z2, &z3, swap)
swap = int32(b)
feSub(&tmp0, &x3, &z3)
feSub(&tmp1, &x2, &z2)
feAdd(&x2, &x2, &z2)
feAdd(&z2, &x3, &z3)
feMul(&z3, &tmp0, &x2)
feMul(&z2, &z2, &tmp1)
feSquare(&tmp0, &tmp1)
feSquare(&tmp1, &x2)
feAdd(&x3, &z3, &z2)
feSub(&z2, &z3, &z2)
feMul(&x2, &tmp1, &tmp0)
feSub(&tmp1, &tmp1, &tmp0)
feSquare(&z2, &z2)
feMul121666(&z3, &tmp1)
feSquare(&x3, &x3)
feAdd(&tmp0, &tmp0, &z3)
feMul(&z3, &x1, &z2)
feMul(&z2, &tmp1, &tmp0)
}
feCSwap(&x2, &x3, swap)
feCSwap(&z2, &z3, swap)
feInvert(&z2, &z2)
feMul(&x2, &x2, &z2)
feToBytes(out, &x2)
}
+12
View File
@@ -0,0 +1,12 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !amd64 || !gc || purego
// +build !amd64 !gc purego
package curve25519
func scalarMult(out, in, base *[32]byte) {
scalarMultGeneric(out, in, base)
}
+16 -48
View File
@@ -2,15 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package curve25519_test
package curve25519
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"testing"
"golang.org/x/crypto/curve25519"
)
const expectedHex = "89161fde887b2b53de549af483940106ecc114d6982daa98256de23bdf77661a"
@@ -21,25 +19,25 @@ func TestX25519Basepoint(t *testing.T) {
for i := 0; i < 200; i++ {
var err error
x, err = curve25519.X25519(x, curve25519.Basepoint)
x, err = X25519(x, Basepoint)
if err != nil {
t.Fatal(err)
}
}
result := hex.EncodeToString(x)
result := fmt.Sprintf("%x", x)
if result != expectedHex {
t.Errorf("incorrect result: got %s, want %s", result, expectedHex)
}
}
func TestLowOrderPoints(t *testing.T) {
scalar := make([]byte, curve25519.ScalarSize)
scalar := make([]byte, ScalarSize)
if _, err := rand.Read(scalar); err != nil {
t.Fatal(err)
}
for i, p := range lowOrderPoints {
out, err := curve25519.X25519(scalar, p)
out, err := X25519(scalar, p)
if err == nil {
t.Errorf("%d: expected error, got nil", i)
}
@@ -50,10 +48,11 @@ func TestLowOrderPoints(t *testing.T) {
}
func TestTestVectors(t *testing.T) {
t.Run("Legacy", func(t *testing.T) { testTestVectors(t, curve25519.ScalarMult) })
t.Run("Generic", func(t *testing.T) { testTestVectors(t, scalarMultGeneric) })
t.Run("Native", func(t *testing.T) { testTestVectors(t, ScalarMult) })
t.Run("X25519", func(t *testing.T) {
testTestVectors(t, func(dst, scalar, point *[32]byte) {
out, err := curve25519.X25519(scalar[:], point[:])
out, err := X25519(scalar[:], point[:])
if err != nil {
t.Fatal(err)
}
@@ -90,53 +89,22 @@ func TestHighBitIgnored(t *testing.T) {
var hi0, hi1 [32]byte
u[31] &= 0x7f
curve25519.ScalarMult(&hi0, &s, &u)
ScalarMult(&hi0, &s, &u)
u[31] |= 0x80
curve25519.ScalarMult(&hi1, &s, &u)
ScalarMult(&hi1, &s, &u)
if !bytes.Equal(hi0[:], hi1[:]) {
t.Errorf("high bit of group point should not affect result")
}
}
var benchmarkSink byte
func BenchmarkScalarBaseMult(b *testing.B) {
var in, out [32]byte
in[0] = 1
func BenchmarkX25519Basepoint(b *testing.B) {
scalar := make([]byte, curve25519.ScalarSize)
if _, err := rand.Read(scalar); err != nil {
b.Fatal(err)
}
b.ResetTimer()
b.SetBytes(32)
for i := 0; i < b.N; i++ {
out, err := curve25519.X25519(scalar, curve25519.Basepoint)
if err != nil {
b.Fatal(err)
}
benchmarkSink ^= out[0]
}
}
func BenchmarkX25519(b *testing.B) {
scalar := make([]byte, curve25519.ScalarSize)
if _, err := rand.Read(scalar); err != nil {
b.Fatal(err)
}
point, err := curve25519.X25519(scalar, curve25519.Basepoint)
if err != nil {
b.Fatal(err)
}
if _, err := rand.Read(scalar); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
out, err := curve25519.X25519(scalar, point)
if err != nil {
b.Fatal(err)
}
benchmarkSink ^= out[0]
ScalarBaseMult(&out, &in)
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package curve25519_test
package curve25519
// lowOrderPoints from libsodium.
// https://github.com/jedisct1/libsodium/blob/65621a1059a37d/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c#L11-L70
+170 -16
View File
@@ -1,7 +1,13 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// In Go 1.13, the ed25519 package was promoted to the standard library as
// crypto/ed25519, and this package became a wrapper for the standard library one.
//
//go:build !go1.13
// +build !go1.13
// Package ed25519 implements the Ed25519 signature algorithm. See
// https://ed25519.cr.yp.to/.
//
@@ -10,13 +16,21 @@
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
//
// This package is a wrapper around the standard library crypto/ed25519 package.
package ed25519
// This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP.
import (
"crypto/ed25519"
"bytes"
"crypto"
cryptorand "crypto/rand"
"crypto/sha512"
"errors"
"io"
"strconv"
"golang.org/x/crypto/ed25519/internal/edwards25519"
)
const (
@@ -31,21 +45,57 @@ const (
)
// PublicKey is the type of Ed25519 public keys.
//
// This type is an alias for crypto/ed25519's PublicKey type.
// See the crypto/ed25519 package for the methods on this type.
type PublicKey = ed25519.PublicKey
type PublicKey []byte
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
//
// This type is an alias for crypto/ed25519's PrivateKey type.
// See the crypto/ed25519 package for the methods on this type.
type PrivateKey = ed25519.PrivateKey
type PrivateKey []byte
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[32:])
return PublicKey(publicKey)
}
// Seed returns the private key seed corresponding to priv. It is provided for
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
// in this package.
func (priv PrivateKey) Seed() []byte {
seed := make([]byte, SeedSize)
copy(seed, priv[:32])
return seed
}
// Sign signs the given message with priv.
// Ed25519 performs two passes over messages to be signed and therefore cannot
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
// indicate the message hasn't been hashed. This can be achieved by passing
// crypto.Hash(0) as the value for opts.
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(priv, message), nil
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
return ed25519.GenerateKey(rand)
if rand == nil {
rand = cryptorand.Reader
}
seed := make([]byte, SeedSize)
if _, err := io.ReadFull(rand, seed); err != nil {
return nil, nil, err
}
privateKey := NewKeyFromSeed(seed)
publicKey := make([]byte, PublicKeySize)
copy(publicKey, privateKey[32:])
return publicKey, privateKey, nil
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
@@ -53,17 +103,121 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
return ed25519.NewKeyFromSeed(seed)
if l := len(seed); l != SeedSize {
panic("ed25519: bad seed length: " + strconv.Itoa(l))
}
digest := sha512.Sum512(seed)
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], digest[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
privateKey := make([]byte, PrivateKeySize)
copy(privateKey, seed)
copy(privateKey[32:], publicKeyBytes[:])
return privateKey
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
return ed25519.Sign(privateKey, message)
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
h := sha512.New()
h.Write(privateKey[:32])
var digest1, messageDigest, hramDigest [64]byte
var expandedSecretKey [32]byte
h.Sum(digest1[:0])
copy(expandedSecretKey[:], digest1[:])
expandedSecretKey[0] &= 248
expandedSecretKey[31] &= 63
expandedSecretKey[31] |= 64
h.Reset()
h.Write(digest1[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(privateKey[32:])
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
signature := make([]byte, SignatureSize)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
return ed25519.Verify(publicKey, message, sig)
if l := len(publicKey); l != PublicKeySize {
panic("ed25519: bad public key length: " + strconv.Itoa(l))
}
if len(sig) != SignatureSize || sig[63]&224 != 0 {
return false
}
var A edwards25519.ExtendedGroupElement
var publicKeyBytes [32]byte
copy(publicKeyBytes[:], publicKey)
if !A.FromBytes(&publicKeyBytes) {
return false
}
edwards25519.FeNeg(&A.X, &A.X)
edwards25519.FeNeg(&A.T, &A.T)
h := sha512.New()
h.Write(sig[:32])
h.Write(publicKey[:])
h.Write(message)
var digest [64]byte
h.Sum(digest[:0])
var hReduced [32]byte
edwards25519.ScReduce(&hReduced, &digest)
var R edwards25519.ProjectiveGroupElement
var s [32]byte
copy(s[:], sig[32:])
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
// the range [0, order) in order to prevent signature malleability.
if !edwards25519.ScMinimal(&s) {
return false
}
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
var checkR [32]byte
R.ToBytes(&checkR)
return bytes.Equal(sig[:32], checkR[:])
}
+74
View File
@@ -0,0 +1,74 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.13
// +build go1.13
// Package ed25519 implements the Ed25519 signature algorithm. See
// https://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
//
// Beginning with Go 1.13, the functionality of this package was moved to the
// standard library as crypto/ed25519. This package only acts as a compatibility
// wrapper.
package ed25519
import (
"crypto/ed25519"
"io"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
SeedSize = 32
)
// PublicKey is the type of Ed25519 public keys.
//
// This type is an alias for crypto/ed25519's PublicKey type.
// See the crypto/ed25519 package for the methods on this type.
type PublicKey = ed25519.PublicKey
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
//
// This type is an alias for crypto/ed25519's PrivateKey type.
// See the crypto/ed25519 package for the methods on this type.
type PrivateKey = ed25519.PrivateKey
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
return ed25519.GenerateKey(rand)
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
return ed25519.NewKeyFromSeed(seed)
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
return ed25519.Sign(privateKey, message)
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
return ed25519.Verify(publicKey, message, sig)
}
+203 -4
View File
@@ -1,22 +1,221 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ed25519_test
import (
ed25519std "crypto/ed25519"
"bufio"
"bytes"
"compress/gzip"
"crypto"
"crypto/rand"
"encoding/hex"
"os"
"strings"
"testing"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ed25519/internal/edwards25519"
)
func TestTypeAlias(t *testing.T) {
public, private, _ := ed25519std.GenerateKey(nil)
type zeroReader struct{}
func (zeroReader) Read(buf []byte) (int, error) {
for i := range buf {
buf[i] = 0
}
return len(buf), nil
}
func TestUnmarshalMarshal(t *testing.T) {
pub, _, _ := ed25519.GenerateKey(rand.Reader)
var A edwards25519.ExtendedGroupElement
var pubBytes [32]byte
copy(pubBytes[:], pub)
if !A.FromBytes(&pubBytes) {
t.Fatalf("ExtendedGroupElement.FromBytes failed")
}
var pub2 [32]byte
A.ToBytes(&pub2)
if pubBytes != pub2 {
t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", pubBytes, pub2)
}
}
func TestSignVerify(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519.GenerateKey(zero)
message := []byte("test message")
sig := ed25519.Sign(private, message)
if !ed25519.Verify(public, message, sig) {
t.Errorf("valid signature rejected")
}
wrongMessage := []byte("wrong message")
if ed25519.Verify(public, wrongMessage, sig) {
t.Errorf("signature of different message accepted")
}
}
func TestCryptoSigner(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519.GenerateKey(zero)
signer := crypto.Signer(private)
publicInterface := signer.Public()
public2, ok := publicInterface.(ed25519.PublicKey)
if !ok {
t.Fatalf("expected PublicKey from Public() but got %T", publicInterface)
}
if !bytes.Equal(public, public2) {
t.Errorf("public keys do not match: original:%x vs Public():%x", public, public2)
}
message := []byte("message")
var noHash crypto.Hash
signature, err := signer.Sign(zero, message, noHash)
if err != nil {
t.Fatalf("error from Sign(): %s", err)
}
if !ed25519.Verify(public, message, signature) {
t.Errorf("Verify failed on signature from Sign()")
}
}
func TestGolden(t *testing.T) {
// sign.input.gz is a selection of test cases from
// https://ed25519.cr.yp.to/python/sign.input
testDataZ, err := os.Open("testdata/sign.input.gz")
if err != nil {
t.Fatal(err)
}
defer testDataZ.Close()
testData, err := gzip.NewReader(testDataZ)
if err != nil {
t.Fatal(err)
}
defer testData.Close()
scanner := bufio.NewScanner(testData)
lineNo := 0
for scanner.Scan() {
lineNo++
line := scanner.Text()
parts := strings.Split(line, ":")
if len(parts) != 5 {
t.Fatalf("bad number of parts on line %d", lineNo)
}
privBytes, _ := hex.DecodeString(parts[0])
pubKey, _ := hex.DecodeString(parts[1])
msg, _ := hex.DecodeString(parts[2])
sig, _ := hex.DecodeString(parts[3])
// The signatures in the test vectors also include the message
// at the end, but we just want R and S.
sig = sig[:ed25519.SignatureSize]
if l := len(pubKey); l != ed25519.PublicKeySize {
t.Fatalf("bad public key length on line %d: got %d bytes", lineNo, l)
}
var priv [ed25519.PrivateKeySize]byte
copy(priv[:], privBytes)
copy(priv[32:], pubKey)
sig2 := ed25519.Sign(priv[:], msg)
if !bytes.Equal(sig, sig2[:]) {
t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
}
if !ed25519.Verify(pubKey, msg, sig2) {
t.Errorf("signature failed to verify on line %d", lineNo)
}
priv2 := ed25519.NewKeyFromSeed(priv[:32])
if !bytes.Equal(priv[:], priv2) {
t.Errorf("recreating key pair gave different private key on line %d: %x vs %x", lineNo, priv[:], priv2)
}
if pubKey2 := priv2.Public().(ed25519.PublicKey); !bytes.Equal(pubKey, pubKey2) {
t.Errorf("recreating key pair gave different public key on line %d: %x vs %x", lineNo, pubKey, pubKey2)
}
if seed := priv2.Seed(); !bytes.Equal(priv[:32], seed) {
t.Errorf("recreating key pair gave different seed on line %d: %x vs %x", lineNo, priv[:32], seed)
}
}
if err := scanner.Err(); err != nil {
t.Fatalf("error reading test data: %s", err)
}
}
func TestMalleability(t *testing.T) {
// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
// that s be in [0, order). This prevents someone from adding a multiple of
// order to s and obtaining a second valid signature for the same message.
msg := []byte{0x54, 0x65, 0x73, 0x74}
sig := []byte{
0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a,
0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b,
0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67,
0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33,
0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d,
}
publicKey := []byte{
0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5,
0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34,
0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
}
if ed25519.Verify(publicKey, msg, sig) {
t.Fatal("non-canonical signature accepted")
}
}
func BenchmarkKeyGeneration(b *testing.B) {
var zero zeroReader
for i := 0; i < b.N; i++ {
if _, _, err := ed25519.GenerateKey(zero); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkSigning(b *testing.B) {
var zero zeroReader
_, priv, err := ed25519.GenerateKey(zero)
if err != nil {
b.Fatal(err)
}
message := []byte("Hello, world!")
b.ResetTimer()
for i := 0; i < b.N; i++ {
ed25519.Sign(priv, message)
}
}
func BenchmarkVerification(b *testing.B) {
var zero zeroReader
pub, priv, err := ed25519.GenerateKey(zero)
if err != nil {
b.Fatal(err)
}
message := []byte("Hello, world!")
signature := ed25519.Sign(priv, message)
b.ResetTimer()
for i := 0; i < b.N; i++ {
ed25519.Verify(pub, message, signature)
}
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.13
// +build go1.13
package ed25519_test
import (
ed25519std "crypto/ed25519"
"golang.org/x/crypto/ed25519"
"testing"
)
func TestTypeAlias(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519std.GenerateKey(zero)
message := []byte("test message")
sig := ed25519.Sign(private, message)
if !ed25519.Verify(public, message, sig) {
t.Errorf("valid signature rejected")
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+5 -6
View File
@@ -1,11 +1,10 @@
module golang.org/x/crypto
go 1.20
go 1.17
require (
golang.org/x/net v0.21.0 // tagx:ignore
golang.org/x/sys v0.25.0
golang.org/x/term v0.24.0
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
golang.org/x/text v0.3.3 // indirect
)
require golang.org/x/text v0.18.0 // indirect
+9 -8
View File
@@ -1,8 +1,9 @@
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+2 -4
View File
@@ -8,7 +8,7 @@
// HKDF is a cryptographic key derivation function (KDF) with the goal of
// expanding limited input keying material into one or more cryptographically
// strong secret keys.
package hkdf
package hkdf // import "golang.org/x/crypto/hkdf"
import (
"crypto/hmac"
@@ -56,9 +56,7 @@ func (f *hkdf) Read(p []byte) (int, error) {
// Fill the rest of the buffer
for len(p) > 0 {
if f.counter > 1 {
f.expander.Reset()
}
f.expander.Reset()
f.expander.Write(f.prev)
f.expander.Write(f.info)
f.expander.Write([]byte{f.counter})
-15
View File
@@ -1,15 +0,0 @@
module internal/poly1305/_asm
go 1.23
require (
github.com/mmcloughlin/avo v0.6.0
golang.org/x/crypto v0.26.0
)
require (
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)
-12
View File
@@ -1,12 +0,0 @@
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
-126
View File
@@ -1,126 +0,0 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
. "github.com/mmcloughlin/avo/build"
. "github.com/mmcloughlin/avo/operand"
. "github.com/mmcloughlin/avo/reg"
_ "golang.org/x/crypto/sha3"
)
//go:generate go run . -out ../sum_amd64.s -pkg poly1305
func main() {
Package("golang.org/x/crypto/internal/poly1305")
ConstraintExpr("gc,!purego")
update()
Generate()
}
func update() {
Implement("update")
Load(Param("state"), RDI)
MOVQ(NewParamAddr("msg_base", 8), RSI)
MOVQ(NewParamAddr("msg_len", 16), R15)
MOVQ(Mem{Base: DI}.Offset(0), R8) // h0
MOVQ(Mem{Base: DI}.Offset(8), R9) // h1
MOVQ(Mem{Base: DI}.Offset(16), R10) // h2
MOVQ(Mem{Base: DI}.Offset(24), R11) // r0
MOVQ(Mem{Base: DI}.Offset(32), R12) // r1
CMPQ(R15, Imm(16))
JB(LabelRef("bytes_between_0_and_15"))
Label("loop")
POLY1305_ADD(RSI, R8, R9, R10)
Label("multiply")
POLY1305_MUL(R8, R9, R10, R11, R12, RBX, RCX, R13, R14)
SUBQ(Imm(16), R15)
CMPQ(R15, Imm(16))
JAE(LabelRef("loop"))
Label("bytes_between_0_and_15")
TESTQ(R15, R15)
JZ(LabelRef("done"))
MOVQ(U32(1), RBX)
XORQ(RCX, RCX)
XORQ(R13, R13)
ADDQ(R15, RSI)
Label("flush_buffer")
SHLQ(Imm(8), RBX, RCX)
SHLQ(Imm(8), RBX)
MOVB(Mem{Base: SI}.Offset(-1), R13B)
XORQ(R13, RBX)
DECQ(RSI)
DECQ(R15)
JNZ(LabelRef("flush_buffer"))
ADDQ(RBX, R8)
ADCQ(RCX, R9)
ADCQ(Imm(0), R10)
MOVQ(U32(16), R15)
JMP(LabelRef("multiply"))
Label("done")
MOVQ(R8, Mem{Base: DI}.Offset(0))
MOVQ(R9, Mem{Base: DI}.Offset(8))
MOVQ(R10, Mem{Base: DI}.Offset(16))
RET()
}
func POLY1305_ADD(msg, h0, h1, h2 GPPhysical) {
ADDQ(Mem{Base: msg}.Offset(0), h0)
ADCQ(Mem{Base: msg}.Offset(8), h1)
ADCQ(Imm(1), h2)
LEAQ(Mem{Base: msg}.Offset(16), msg)
}
func POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3 GPPhysical) {
MOVQ(r0, RAX)
MULQ(h0)
MOVQ(RAX, t0)
MOVQ(RDX, t1)
MOVQ(r0, RAX)
MULQ(h1)
ADDQ(RAX, t1)
ADCQ(Imm(0), RDX)
MOVQ(r0, t2)
IMULQ(h2, t2)
ADDQ(RDX, t2)
MOVQ(r1, RAX)
MULQ(h0)
ADDQ(RAX, t1)
ADCQ(Imm(0), RDX)
MOVQ(RDX, h0)
MOVQ(r1, t3)
IMULQ(h2, t3)
MOVQ(r1, RAX)
MULQ(h1)
ADDQ(RAX, t2)
ADCQ(RDX, t3)
ADDQ(h0, t2)
ADCQ(Imm(0), t3)
MOVQ(t0, h0)
MOVQ(t1, h1)
MOVQ(t2, h2)
ANDQ(Imm(3), h2)
MOVQ(t2, t0)
ANDQ(I32(-4), t0)
ADDQ(t0, h0)
ADCQ(t3, h1)
ADCQ(Imm(0), h2)
SHRQ(Imm(2), t3, t2)
SHRQ(Imm(2), t3)
ADDQ(t2, h0)
ADCQ(t3, h1)
ADCQ(Imm(0), h2)
}

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