Compare commits
149
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18e0be4596 | ||
|
|
7a4a4d6bea | ||
|
|
1f7c531b64 | ||
|
|
234d0b5a5f | ||
|
|
a083cb74b6 | ||
|
|
b4dcfb54b8 | ||
|
|
b853e4b620 | ||
|
|
3f62bf119e | ||
|
|
86efde54dc | ||
|
|
a6cdac6084 | ||
|
|
39dc44e69c | ||
|
|
afebf4cb4e | ||
|
|
89f4e9bb5b | ||
|
|
71488c48c2 | ||
|
|
82adefa711 | ||
|
|
c757c9851f | ||
|
|
593c81af8a | ||
|
|
46efc8bc62 | ||
|
|
e0a54fdc4b | ||
|
|
f44d03d253 | ||
|
|
5ed494470b | ||
|
|
b07833c067 | ||
|
|
d701c51f7e | ||
|
|
999d053994 | ||
|
|
90f76b8ffe | ||
|
|
b53964a1ca | ||
|
|
626e40fc98 | ||
|
|
31914c699b | ||
|
|
f2135b814c | ||
|
|
ff03dafdb0 | ||
|
|
10b54ffa51 | ||
|
|
0f039f3a4a | ||
|
|
b27e1504e3 | ||
|
|
c1a3b97d70 | ||
|
|
b9229ecc70 | ||
|
|
e557b08ec2 | ||
|
|
4044611da8 | ||
|
|
e8ad5cb7aa | ||
|
|
fe1af3a418 | ||
|
|
243e02a382 | ||
|
|
cdce021fa6 | ||
|
|
d9474cc485 | ||
|
|
7626c50256 | ||
|
|
0471e7969e | ||
|
|
6435c37ab6 | ||
|
|
7d695da948 | ||
|
|
5b7f841599 | ||
|
|
0b316e7ee4 | ||
|
|
55aec0a86b | ||
|
|
5f2de1a9f1 | ||
|
|
45460e0797 | ||
|
|
d37c95e27d | ||
|
|
e2ffffe738 | ||
|
|
60e158ad30 | ||
|
|
1b77d23dc8 | ||
|
|
3872a2bc69 | ||
|
|
9f72eccdf6 | ||
|
|
8f405a4109 | ||
|
|
bb41b3dbe6 | ||
|
|
e04e721997 | ||
|
|
b315afd69a | ||
|
|
d4a85f4749 | ||
|
|
6f39c5226a | ||
|
|
4c4d20b72c | ||
|
|
e3e62d9601 | ||
|
|
5adb68b3e6 | ||
|
|
9beb694f97 | ||
|
|
e5306b2f3e | ||
|
|
d2fe592df1 | ||
|
|
d59570d1a3 | ||
|
|
4aab0d9d6a | ||
|
|
a1c0d99298 | ||
|
|
3c7c86938f | ||
|
|
533fb3f7e4 | ||
|
|
abbc44d451 | ||
|
|
e052873987 | ||
|
|
b61cf853a8 | ||
|
|
9c2cd33e8d | ||
|
|
890731877d | ||
|
|
ffd87b4878 | ||
|
|
4e7a7384ec | ||
|
|
b25012b37b | ||
|
|
6c195c8a97 | ||
|
|
f717e29698 | ||
|
|
e7c36ccb47 | ||
|
|
0fb843a472 | ||
|
|
e3d1254f1e | ||
|
|
a1ce0fee12 | ||
|
|
a749d17980 | ||
|
|
7ee59707ff | ||
|
|
44decbfe70 | ||
|
|
b8a14a8d65 | ||
|
|
9d9d507896 | ||
|
|
fd0b90d21f | ||
|
|
b9e53593a6 | ||
|
|
cc0e4fc1d4 | ||
|
|
a8e9237a21 | ||
|
|
03ca0dcccb | ||
|
|
8400f4a938 | ||
|
|
81c6cb34a8 | ||
|
|
982eaa62df | ||
|
|
159944f128 | ||
|
|
a408498e55 | ||
|
|
cab0f71854 | ||
|
|
2f26647a79 | ||
|
|
e08b06753d | ||
|
|
7d0074ccc6 | ||
|
|
506e022208 | ||
|
|
7dacc380ba | ||
|
|
19acf81bd7 | ||
|
|
3a1c6b4b61 | ||
|
|
f4602e4040 | ||
|
|
4e0068c009 | ||
|
|
e79546e28b | ||
|
|
f91f7a7c31 | ||
|
|
2df4153a03 | ||
|
|
bcf6a849ef | ||
|
|
b4f2b62076 | ||
|
|
79ec3a51fc | ||
|
|
122a78f140 | ||
|
|
c0531f9c34 | ||
|
|
0997000b45 | ||
|
|
017a1aaa2d | ||
|
|
cf29fa96f8 | ||
|
|
0b7aa0cfb0 | ||
|
|
1faea2975c | ||
|
|
627cb894b6 | ||
|
|
dca4914afe | ||
|
|
1336e21bd6 | ||
|
|
2beaa59a3c | ||
|
|
66c3d8ce71 | ||
|
|
ddb4e80c6a | ||
|
|
f4d47b0db5 | ||
|
|
96dc232fbd | ||
|
|
8c9ba31836 | ||
|
|
559e062ce8 | ||
|
|
5307a0ce6d | ||
|
|
9d779377cf | ||
|
|
8f580defa0 | ||
|
|
a4d1237429 | ||
|
|
b8d8dae13d | ||
|
|
f5a2eabcab | ||
|
|
44ecf3af99 | ||
|
|
ef5341b706 | ||
|
|
b999374650 | ||
|
|
c247dead11 | ||
|
|
1fda73153f | ||
|
|
1b4c3d2e8c | ||
|
|
b903b535d3 |
+2
-2
@@ -221,7 +221,7 @@ func (c *Client) directoryURL() string {
|
|||||||
|
|
||||||
// CreateCert was part of the old version of ACME. It is incompatible with RFC 8555.
|
// CreateCert was part of the old version of ACME. It is incompatible with RFC 8555.
|
||||||
//
|
//
|
||||||
// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateOrderCert.
|
// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateCertFromOrder.
|
||||||
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
|
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
|
||||||
return nil, "", errPreRFC
|
return nil, "", errPreRFC
|
||||||
}
|
}
|
||||||
@@ -690,7 +690,7 @@ func (c *Client) addNonce(h http.Header) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
|
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
|
||||||
r, err := http.NewRequest("HEAD", url, nil)
|
r, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -734,7 +734,7 @@ func TestTLSALPN01ChallengeCert(t *testing.T) {
|
|||||||
if !acmeExts[0].Critical {
|
if !acmeExts[0].Critical {
|
||||||
t.Errorf("acmeExt.Critical = %v; want true", acmeExts[0].Critical)
|
t.Errorf("acmeExt.Critical = %v; want true", acmeExts[0].Critical)
|
||||||
}
|
}
|
||||||
if bytes.Compare(acmeExts[0].Value, extValue) != 0 {
|
if !bytes.Equal(acmeExts[0].Value, extValue) {
|
||||||
t.Errorf("acmeExt.Value = %v; want %v", acmeExts[0].Value, extValue)
|
t.Errorf("acmeExt.Value = %v; want %v", acmeExts[0].Value, extValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-40
@@ -134,7 +134,8 @@ type Manager struct {
|
|||||||
// RenewBefore optionally specifies how early certificates should
|
// RenewBefore optionally specifies how early certificates should
|
||||||
// be renewed before they expire.
|
// be renewed before they expire.
|
||||||
//
|
//
|
||||||
// If zero, they're renewed 30 days before expiration.
|
// If zero, they're renewed at the lesser of 30 days or
|
||||||
|
// 1/3 of the certificate lifetime.
|
||||||
RenewBefore time.Duration
|
RenewBefore time.Duration
|
||||||
|
|
||||||
// Client is used to perform low-level operations, such as account registration
|
// Client is used to perform low-level operations, such as account registration
|
||||||
@@ -247,10 +248,6 @@ func (m *Manager) TLSConfig() *tls.Config {
|
|||||||
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
||||||
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01.
|
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01.
|
||||||
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
if m.Prompt == nil {
|
|
||||||
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
name := hello.ServerName
|
name := hello.ServerName
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, errors.New("acme/autocert: missing server name")
|
return nil, errors.New("acme/autocert: missing server name")
|
||||||
@@ -464,7 +461,7 @@ func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error
|
|||||||
leaf: cert.Leaf,
|
leaf: cert.Leaf,
|
||||||
}
|
}
|
||||||
m.state[ck] = s
|
m.state[ck] = s
|
||||||
m.startRenew(ck, s.key, s.leaf.NotAfter)
|
m.startRenew(ck, s.key, s.leaf.NotBefore, s.leaf.NotAfter)
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,24 +564,21 @@ func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
|||||||
// If the domain is already being verified, it waits for the existing verification to complete.
|
// If the domain is already being verified, it waits for the existing verification to complete.
|
||||||
// Either way, createCert blocks for the duration of the whole process.
|
// Either way, createCert blocks for the duration of the whole process.
|
||||||
func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
||||||
// TODO: maybe rewrite this whole piece using sync.Once
|
state, owner, err := m.certState(ck)
|
||||||
state, err := m.certState(ck)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// state may exist if another goroutine is already working on it
|
// If another goroutine is already working on this state, wait for it
|
||||||
// in which case just wait for it to finish
|
// to finish by taking the read lock
|
||||||
if !state.locked {
|
if !owner {
|
||||||
state.RLock()
|
state.RLock()
|
||||||
defer state.RUnlock()
|
defer state.RUnlock()
|
||||||
return state.tlscert()
|
return state.tlscert()
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are the first; state is locked.
|
// We are the first to work on this certKey, so state is write-locked.
|
||||||
// Unblock the readers when domain ownership is verified
|
// Unblock the readers when our work is complete.
|
||||||
// and we got the cert or the process failed.
|
|
||||||
defer state.Unlock()
|
defer state.Unlock()
|
||||||
state.locked = false
|
|
||||||
|
|
||||||
der, leaf, err := m.authorizedCert(ctx, state.key, ck)
|
der, leaf, err := m.authorizedCert(ctx, state.key, ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -610,14 +604,19 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
|
|||||||
}
|
}
|
||||||
state.cert = der
|
state.cert = der
|
||||||
state.leaf = leaf
|
state.leaf = leaf
|
||||||
m.startRenew(ck, state.key, state.leaf.NotAfter)
|
m.startRenew(ck, state.key, state.leaf.NotBefore, state.leaf.NotAfter)
|
||||||
return state.tlscert()
|
return state.tlscert()
|
||||||
}
|
}
|
||||||
|
|
||||||
// certState returns a new or existing certState.
|
// certState returns a new or existing certState along with a boolean
|
||||||
// If a new certState is returned, state.exist is false and the state is locked.
|
// indicating whether the caller is the owner of the state.
|
||||||
|
//
|
||||||
|
// The owner of the state is responsible for performing the ACME work and
|
||||||
|
// must unlock the state's write lock when done. Non-owner callers should
|
||||||
|
// wait on the state's read lock for the owner to finish.
|
||||||
|
//
|
||||||
// The returned error is non-nil only in the case where a new state could not be created.
|
// The returned error is non-nil only in the case where a new state could not be created.
|
||||||
func (m *Manager) certState(ck certKey) (*certState, error) {
|
func (m *Manager) certState(ck certKey) (*certState, bool, error) {
|
||||||
m.stateMu.Lock()
|
m.stateMu.Lock()
|
||||||
defer m.stateMu.Unlock()
|
defer m.stateMu.Unlock()
|
||||||
if m.state == nil {
|
if m.state == nil {
|
||||||
@@ -625,7 +624,7 @@ func (m *Manager) certState(ck certKey) (*certState, error) {
|
|||||||
}
|
}
|
||||||
// existing state
|
// existing state
|
||||||
if state, ok := m.state[ck]; ok {
|
if state, ok := m.state[ck]; ok {
|
||||||
return state, nil
|
return state, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// new locked state
|
// new locked state
|
||||||
@@ -639,16 +638,13 @@ func (m *Manager) certState(ck certKey) (*certState, error) {
|
|||||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
state := &certState{
|
state := &certState{key: key}
|
||||||
key: key,
|
|
||||||
locked: true,
|
|
||||||
}
|
|
||||||
state.Lock() // will be unlocked by m.certState caller
|
state.Lock() // will be unlocked by m.certState caller
|
||||||
m.state[ck] = state
|
m.state[ck] = state
|
||||||
return state, nil
|
return state, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
||||||
@@ -675,7 +671,7 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
chain, _, err := client.CreateCertFromOrder(ctx, o, csr, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -908,7 +904,7 @@ func httpTokenCacheKey(tokenPath string) string {
|
|||||||
//
|
//
|
||||||
// The key argument is a certificate private key.
|
// The key argument is a certificate private key.
|
||||||
// The exp argument is the cert expiration time (NotAfter).
|
// The exp argument is the cert expiration time (NotAfter).
|
||||||
func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) {
|
func (m *Manager) startRenew(ck certKey, key crypto.Signer, notBefore, notAfter time.Time) {
|
||||||
m.renewalMu.Lock()
|
m.renewalMu.Lock()
|
||||||
defer m.renewalMu.Unlock()
|
defer m.renewalMu.Unlock()
|
||||||
if m.renewal[ck] != nil {
|
if m.renewal[ck] != nil {
|
||||||
@@ -920,7 +916,7 @@ func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) {
|
|||||||
}
|
}
|
||||||
dr := &domainRenewal{m: m, ck: ck, key: key}
|
dr := &domainRenewal{m: m, ck: ck, key: key}
|
||||||
m.renewal[ck] = dr
|
m.renewal[ck] = dr
|
||||||
dr.start(exp)
|
dr.start(notBefore, notAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopRenew stops all currently running cert renewal timers.
|
// stopRenew stops all currently running cert renewal timers.
|
||||||
@@ -1028,13 +1024,6 @@ func (m *Manager) hostPolicy() HostPolicy {
|
|||||||
return defaultHostPolicy
|
return defaultHostPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) renewBefore() time.Duration {
|
|
||||||
if m.RenewBefore > renewJitter {
|
|
||||||
return m.RenewBefore
|
|
||||||
}
|
|
||||||
return 720 * time.Hour // 30 days
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) now() time.Time {
|
func (m *Manager) now() time.Time {
|
||||||
if m.nowFunc != nil {
|
if m.nowFunc != nil {
|
||||||
return m.nowFunc()
|
return m.nowFunc()
|
||||||
@@ -1045,10 +1034,9 @@ func (m *Manager) now() time.Time {
|
|||||||
// certState is ready when its mutex is unlocked for reading.
|
// certState is ready when its mutex is unlocked for reading.
|
||||||
type certState struct {
|
type certState struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
locked bool // locked for read/write
|
key crypto.Signer // private key for cert
|
||||||
key crypto.Signer // private key for cert
|
cert [][]byte // DER encoding
|
||||||
cert [][]byte // DER encoding
|
leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil
|
||||||
leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tlscert creates a tls.Certificate from s.key and s.cert.
|
// tlscert creates a tls.Certificate from s.key and s.cert.
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ func TestGetCertificate(t *testing.T) {
|
|||||||
prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
|
prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
|
||||||
man.Prompt = nil
|
man.Prompt = nil
|
||||||
},
|
},
|
||||||
expectError: "Manager.Prompt not set",
|
expectError: "missing Manager.Prompt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "trailingDot",
|
name: "trailingDot",
|
||||||
@@ -513,6 +513,33 @@ func TestGetCertificate_failedAttempt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGetCertificate_concurrent guards against a data race on certState
|
||||||
|
// fields between the goroutine performing the ACME work for a domain and
|
||||||
|
// the other goroutines waiting on the result. See golang/go#80119.
|
||||||
|
func TestGetCertificate_concurrent(t *testing.T) {
|
||||||
|
// An unreachable directory URL makes the owner goroutine fail
|
||||||
|
// quickly inside createCert, so its writes overlap with the
|
||||||
|
// non-owner goroutines reaching the same code path.
|
||||||
|
m := &Manager{
|
||||||
|
Prompt: AcceptTOS,
|
||||||
|
Client: &acme.Client{DirectoryURL: "http://127.0.0.1:1/"},
|
||||||
|
}
|
||||||
|
defer m.stopRenew()
|
||||||
|
|
||||||
|
start := make(chan struct{})
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 64; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
<-start
|
||||||
|
m.GetCertificate(clientHelloInfo(exampleDomain, algECDSA))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
close(start)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
func TestRevokeFailedAuthz(t *testing.T) {
|
func TestRevokeFailedAuthz(t *testing.T) {
|
||||||
ca := acmetest.NewCAServer(t)
|
ca := acmetest.NewCAServer(t)
|
||||||
// Make the authz unfulfillable on the client side, so it will be left
|
// Make the authz unfulfillable on the client side, so it will be left
|
||||||
@@ -561,9 +588,9 @@ func TestHTTPHandlerDefaultFallback(t *testing.T) {
|
|||||||
{"GET", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
|
{"GET", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
|
||||||
{"GET", "http://example.org:80/foo?a=b", 302, "https://example.org:443/foo?a=b"},
|
{"GET", "http://example.org:80/foo?a=b", 302, "https://example.org:443/foo?a=b"},
|
||||||
{"GET", "http://example.org:80/foo%20bar", 302, "https://example.org:443/foo%20bar"},
|
{"GET", "http://example.org:80/foo%20bar", 302, "https://example.org:443/foo%20bar"},
|
||||||
{"GET", "http://[2602:d1:xxxx::c60a]:1234", 302, "https://[2602:d1:xxxx::c60a]:443/"},
|
{"GET", "http://[2602:d1:abcd::c60a]:1234", 302, "https://[2602:d1:abcd::c60a]:443/"},
|
||||||
{"GET", "http://[2602:d1:xxxx::c60a]", 302, "https://[2602:d1:xxxx::c60a]/"},
|
{"GET", "http://[2602:d1:abcd::c60a]", 302, "https://[2602:d1:abcd::c60a]/"},
|
||||||
{"GET", "http://[2602:d1:xxxx::c60a]/foo?a=b", 302, "https://[2602:d1:xxxx::c60a]/foo?a=b"},
|
{"GET", "http://[2602:d1:abcd::c60a]/foo?a=b", 302, "https://[2602:d1:abcd::c60a]/foo?a=b"},
|
||||||
{"HEAD", "http://example.org", 302, "https://example.org/"},
|
{"HEAD", "http://example.org", 302, "https://example.org/"},
|
||||||
{"HEAD", "http://example.org/foo", 302, "https://example.org/foo"},
|
{"HEAD", "http://example.org/foo", 302, "https://example.org/foo"},
|
||||||
{"HEAD", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
|
{"HEAD", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ func (ca *CAServer) URL() string {
|
|||||||
return ca.url
|
return ca.url
|
||||||
}
|
}
|
||||||
|
|
||||||
// Roots returns a pool cointaining the CA root.
|
// Roots returns a pool containing the CA root.
|
||||||
func (ca *CAServer) Roots() *x509.CertPool {
|
func (ca *CAServer) Roots() *x509.CertPool {
|
||||||
if ca.url == "" {
|
if ca.url == "" {
|
||||||
panic("Roots called before Start")
|
panic("Roots called before Start")
|
||||||
@@ -239,7 +239,8 @@ type discovery struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type discoveryMeta struct {
|
type discoveryMeta struct {
|
||||||
ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"`
|
Terms string `json:"termsOfService,omitempty"`
|
||||||
|
ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type challenge struct {
|
type challenge struct {
|
||||||
@@ -281,6 +282,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
|
|||||||
NewAccount: ca.serverURL("/new-account"),
|
NewAccount: ca.serverURL("/new-account"),
|
||||||
NewOrder: ca.serverURL("/new-order"),
|
NewOrder: ca.serverURL("/new-order"),
|
||||||
Meta: discoveryMeta{
|
Meta: discoveryMeta{
|
||||||
|
Terms: ca.serverURL("/terms"),
|
||||||
ExternalAccountRequired: ca.eabRequired,
|
ExternalAccountRequired: ca.eabRequired,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-17
@@ -11,9 +11,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// renewJitter is the maximum deviation from Manager.RenewBefore.
|
|
||||||
const renewJitter = time.Hour
|
|
||||||
|
|
||||||
// domainRenewal tracks the state used by the periodic timers
|
// domainRenewal tracks the state used by the periodic timers
|
||||||
// renewing a single domain's cert.
|
// renewing a single domain's cert.
|
||||||
type domainRenewal struct {
|
type domainRenewal struct {
|
||||||
@@ -30,13 +27,13 @@ type domainRenewal struct {
|
|||||||
// defined by the certificate expiration time exp.
|
// defined by the certificate expiration time exp.
|
||||||
//
|
//
|
||||||
// If the timer is already started, calling start is a noop.
|
// If the timer is already started, calling start is a noop.
|
||||||
func (dr *domainRenewal) start(exp time.Time) {
|
func (dr *domainRenewal) start(notBefore, notAfter time.Time) {
|
||||||
dr.timerMu.Lock()
|
dr.timerMu.Lock()
|
||||||
defer dr.timerMu.Unlock()
|
defer dr.timerMu.Unlock()
|
||||||
if dr.timer != nil {
|
if dr.timer != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
|
dr.timer = time.AfterFunc(dr.next(notBefore, notAfter), dr.renew)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop stops the cert renewal timer and waits for any in-flight calls to renew
|
// stop stops the cert renewal timer and waits for any in-flight calls to renew
|
||||||
@@ -79,7 +76,7 @@ func (dr *domainRenewal) renew() {
|
|||||||
// TODO: rotate dr.key at some point?
|
// TODO: rotate dr.key at some point?
|
||||||
next, err := dr.do(ctx)
|
next, err := dr.do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
next = renewJitter / 2
|
next = time.Hour / 2
|
||||||
next += time.Duration(pseudoRand.int63n(int64(next)))
|
next += time.Duration(pseudoRand.int63n(int64(next)))
|
||||||
}
|
}
|
||||||
testDidRenewLoop(next, err)
|
testDidRenewLoop(next, err)
|
||||||
@@ -107,8 +104,8 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
|||||||
// a race is likely unavoidable in a distributed environment
|
// a race is likely unavoidable in a distributed environment
|
||||||
// but we try nonetheless
|
// but we try nonetheless
|
||||||
if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
|
if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
|
||||||
next := dr.next(tlscert.Leaf.NotAfter)
|
next := dr.next(tlscert.Leaf.NotBefore, tlscert.Leaf.NotAfter)
|
||||||
if next > dr.m.renewBefore()+renewJitter {
|
if next > 0 {
|
||||||
signer, ok := tlscert.PrivateKey.(crypto.Signer)
|
signer, ok := tlscert.PrivateKey.(crypto.Signer)
|
||||||
if ok {
|
if ok {
|
||||||
state := &certState{
|
state := &certState{
|
||||||
@@ -139,18 +136,23 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
dr.updateState(state)
|
dr.updateState(state)
|
||||||
return dr.next(leaf.NotAfter), nil
|
return dr.next(leaf.NotBefore, leaf.NotAfter), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
|
// next returns the wait time before the next renewal should start.
|
||||||
d := expiry.Sub(dr.m.now()) - dr.m.renewBefore()
|
// If manager.RenewBefore is set, it uses that capped at 30 days,
|
||||||
// add a bit of randomness to renew deadline
|
// otherwise it uses a default of 1/3 of the cert lifetime.
|
||||||
n := pseudoRand.int63n(int64(renewJitter))
|
// It builds in a jitter of 10% of the renew threshold, capped at 1 hour.
|
||||||
d -= time.Duration(n)
|
func (dr *domainRenewal) next(notBefore, notAfter time.Time) time.Duration {
|
||||||
if d < 0 {
|
threshold := min(notAfter.Sub(notBefore)/3, 30*24*time.Hour)
|
||||||
return 0
|
if dr.m.RenewBefore > 0 {
|
||||||
|
threshold = min(dr.m.RenewBefore, 30*24*time.Hour)
|
||||||
}
|
}
|
||||||
return d
|
maxJitter := min(threshold/10, time.Hour)
|
||||||
|
jitter := pseudoRand.int63n(int64(maxJitter))
|
||||||
|
renewAt := notAfter.Add(-(threshold - time.Duration(jitter)))
|
||||||
|
renewWait := renewAt.Sub(dr.m.now())
|
||||||
|
return max(0, renewWait)
|
||||||
}
|
}
|
||||||
|
|
||||||
var testDidRenewLoop = func(next time.Duration, err error) {}
|
var testDidRenewLoop = func(next time.Duration, err error) {}
|
||||||
|
|||||||
@@ -17,27 +17,60 @@ import (
|
|||||||
|
|
||||||
func TestRenewalNext(t *testing.T) {
|
func TestRenewalNext(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
man := &Manager{
|
nowFn := func() time.Time { return now }
|
||||||
RenewBefore: 7 * 24 * time.Hour,
|
|
||||||
nowFunc: func() time.Time { return now },
|
|
||||||
}
|
|
||||||
defer man.stopRenew()
|
|
||||||
tt := []struct {
|
tt := []struct {
|
||||||
expiry time.Time
|
name string
|
||||||
min, max time.Duration
|
renewBefore time.Duration // arg to Manager
|
||||||
|
// leaf cert validity
|
||||||
|
notBefore time.Time
|
||||||
|
validFor time.Duration
|
||||||
|
// wait time
|
||||||
|
waitMin, waitMax time.Duration
|
||||||
}{
|
}{
|
||||||
{now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
|
{"default renewal, 1h cert, valid",
|
||||||
{now.Add(time.Hour), 0, 1},
|
0, now, time.Hour, 40 * time.Minute, 50 * time.Minute},
|
||||||
{now, 0, 1},
|
{"default renewal, 1h cert, should renew",
|
||||||
{now.Add(-time.Hour), 0, 1},
|
0, now.Add(-50 * time.Minute), time.Hour, 0, 0},
|
||||||
|
{"default renewal, 1h cert, expired",
|
||||||
|
0, now.Add(-400 * 24 * time.Hour), time.Hour, 0, 0},
|
||||||
|
{"default renewal, 6d cert, valid",
|
||||||
|
0, now, 6 * 24 * time.Hour, 4 * 24 * time.Hour, (4*24 + 1) * time.Hour},
|
||||||
|
{"default renewal, 6d cert, should renew",
|
||||||
|
0, now.Add(-5 * 24 * time.Hour), 6 * 24 * time.Hour, 0, 0},
|
||||||
|
{"default renewal, 6d cert, expired",
|
||||||
|
0, now.Add(-400 * 24 * time.Hour), 6 * 24 * time.Hour, 0, 0},
|
||||||
|
{"default renewal, 90d cert, valid",
|
||||||
|
0, now, 90 * 24 * time.Hour, 60 * 24 * time.Hour, (60*24 + 1) * time.Hour},
|
||||||
|
{"default renewal, 90d cert, should renew",
|
||||||
|
0, now.Add(-70 * 24 * time.Hour), 90 * 24 * time.Hour, 0, 0},
|
||||||
|
{"default renewal, 90d cert, expired",
|
||||||
|
0, now.Add(-400 * 24 * time.Hour), 90 * 24 * time.Hour, 0, 0},
|
||||||
|
{"default renewal, 398d cert, valid",
|
||||||
|
0, now, 398 * 24 * time.Hour, (368 * 24) * time.Hour, (368*24 + 1) * time.Hour},
|
||||||
|
{"default renewal, 398d cert, should renew",
|
||||||
|
0, now.Add(-378 * 24 * time.Hour), 398 * 24 * time.Hour, 0, 0},
|
||||||
|
{"default renewal, 398d cert, expired",
|
||||||
|
0, now.Add(-400 * 24 * time.Hour), 398 * 24 * time.Hour, 0, 0},
|
||||||
|
{"7d renewal, 90d cert, valid",
|
||||||
|
7 * 24 * time.Hour, now, 90 * 24 * time.Hour, 83 * 24 * time.Hour, (83*24 + 1) * time.Hour},
|
||||||
|
{"7d renewal, 90d cert, should not renew",
|
||||||
|
7 * 24 * time.Hour, now.Add(-70 * 24 * time.Hour), 90 * 24 * time.Hour, 13 * 24 * time.Hour, (13*24 + 1) * time.Hour},
|
||||||
|
{"7d renewal, 90d cert, should renew",
|
||||||
|
7 * 24 * time.Hour, now.Add(-85 * 24 * time.Hour), 90 * 24 * time.Hour, 0, 0},
|
||||||
|
{"7d renewal, 90d cert, expired",
|
||||||
|
7 * 24 * time.Hour, now.Add(-400 * 24 * time.Hour), 90 * 24 * time.Hour, 0, 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
dr := &domainRenewal{m: man}
|
for _, test := range tt {
|
||||||
for i, test := range tt {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
next := dr.next(test.expiry)
|
dr := &domainRenewal{m: &Manager{RenewBefore: test.renewBefore, nowFunc: nowFn}}
|
||||||
if next < test.min || test.max < next {
|
defer dr.m.stopRenew()
|
||||||
t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
|
|
||||||
}
|
next := dr.next(test.notBefore, test.notBefore.Add(test.validFor))
|
||||||
|
if next < test.waitMin || next > test.waitMax {
|
||||||
|
t.Errorf("expected wait time: %v <= %v <= %v", test.waitMin, next, test.waitMax)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,9 +180,6 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
|
|||||||
|
|
||||||
// set internal state to an almost expired cert
|
// set internal state to an almost expired cert
|
||||||
oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
|
oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
|
oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -239,7 +269,7 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// trigger renew
|
// trigger renew
|
||||||
man.startRenew(exampleCertKey, s.key, s.leaf.NotAfter)
|
man.startRenew(exampleCertKey, s.key, s.leaf.NotBefore, s.leaf.NotAfter)
|
||||||
<-renewed
|
<-renewed
|
||||||
func() {
|
func() {
|
||||||
man.renewalMu.Lock()
|
man.renewalMu.Lock()
|
||||||
|
|||||||
+2
-2
@@ -128,7 +128,7 @@ func wantStatus(codes ...int) resOkay {
|
|||||||
func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
|
func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
|
||||||
retry := c.retryTimer()
|
retry := c.retryTimer()
|
||||||
for {
|
for {
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
|
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,9 +182,9 @@ func (p *prober) runOrder(ctx context.Context, identifiers []acme.AuthzID) {
|
|||||||
log.Fatalf("WaitOrder(%q): %v", o.URI, err)
|
log.Fatalf("WaitOrder(%q): %v", o.URI, err)
|
||||||
}
|
}
|
||||||
csr, certkey := newCSR(identifiers)
|
csr, certkey := newCSR(identifiers)
|
||||||
der, curl, err := p.client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
der, curl, err := p.client.CreateCertFromOrder(ctx, o, csr, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("CreateOrderCert: %v", err)
|
log.Fatalf("CreateCertFromOrder: %v", err)
|
||||||
}
|
}
|
||||||
log.Printf("cert URL: %s", curl)
|
log.Printf("cert URL: %s", curl)
|
||||||
if err := checkCert(der, identifiers); err != nil {
|
if err := checkCert(der, identifiers); err != nil {
|
||||||
@@ -247,9 +247,9 @@ func (p *prober) runPreauthz(ctx context.Context, identifiers []acme.AuthzID) {
|
|||||||
log.Fatalf("WaitOrder(%q): %v", o.URI, err)
|
log.Fatalf("WaitOrder(%q): %v", o.URI, err)
|
||||||
}
|
}
|
||||||
csr, certkey := newCSR(identifiers)
|
csr, certkey := newCSR(identifiers)
|
||||||
der, curl, err := p.client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
der, curl, err := p.client.CreateCertFromOrder(ctx, o, csr, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("CreateOrderCert: %v", err)
|
log.Fatalf("CreateCertFromOrder: %v", err)
|
||||||
}
|
}
|
||||||
log.Printf("cert URL: %s", curl)
|
log.Printf("cert URL: %s", curl)
|
||||||
if err := checkCert(der, identifiers); err != nil {
|
if err := checkCert(der, identifiers); err != nil {
|
||||||
|
|||||||
+5
-8
@@ -103,9 +103,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, ur
|
|||||||
}
|
}
|
||||||
payload = base64.RawURLEncoding.EncodeToString(cs)
|
payload = base64.RawURLEncoding.EncodeToString(cs)
|
||||||
}
|
}
|
||||||
hash := sha.New()
|
sig, err := jwsSign(key, sha, []byte(phead+"."+payload))
|
||||||
hash.Write([]byte(phead + "." + payload))
|
|
||||||
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -195,14 +193,13 @@ func jwkEncode(pub crypto.PublicKey) (string, error) {
|
|||||||
return "", ErrUnsupportedKey
|
return "", ErrUnsupportedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwsSign signs the digest using the given key.
|
// jwsSign signs the payload using the given key.
|
||||||
// The hash is unused for ECDSA keys.
|
func jwsSign(key crypto.Signer, hash crypto.Hash, payload []byte) ([]byte, error) {
|
||||||
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
|
||||||
switch pub := key.Public().(type) {
|
switch pub := key.Public().(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
return key.Sign(rand.Reader, digest, hash)
|
return crypto.SignMessage(key, rand.Reader, payload, hash)
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
sigASN1, err := key.Sign(rand.Reader, digest, hash)
|
sigASN1, err := crypto.SignMessage(key, rand.Reader, payload, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-5
@@ -14,6 +14,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
@@ -353,6 +354,22 @@ func (s *customTestSigner) Sign(io.Reader, []byte, crypto.SignerOpts) ([]byte, e
|
|||||||
return s.sig, nil
|
return s.sig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type customTestMessageSigner struct {
|
||||||
|
customTestSigner
|
||||||
|
msg []byte
|
||||||
|
opts crypto.SignerOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *customTestMessageSigner) Sign(io.Reader, []byte, crypto.SignerOpts) ([]byte, error) {
|
||||||
|
return nil, errors.New("customTestSigner: Sign() called instead of SignMessage()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *customTestMessageSigner) SignMessage(_ io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||||
|
s.msg = msg
|
||||||
|
s.opts = opts
|
||||||
|
return s.sig, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestJWSEncodeJSONCustom(t *testing.T) {
|
func TestJWSEncodeJSONCustom(t *testing.T) {
|
||||||
claims := struct{ Msg string }{"hello"}
|
claims := struct{ Msg string }{"hello"}
|
||||||
const (
|
const (
|
||||||
@@ -391,24 +408,33 @@ func TestJWSEncodeJSONCustom(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
tt := []struct {
|
tt := []struct {
|
||||||
alg, phead string
|
name, phead string
|
||||||
pub crypto.PublicKey
|
pub crypto.PublicKey
|
||||||
stdsig, jwsig string
|
stdsig, jwsig string
|
||||||
|
msgSigner bool
|
||||||
}{
|
}{
|
||||||
{"ES256", es256phead, testKeyEC.Public(), es256stdsig, es256jwsig},
|
{"ES256", es256phead, testKeyEC.Public(), es256stdsig, es256jwsig, false},
|
||||||
{"RS256", rs256phead, testKey.Public(), testsig, testsig},
|
{"RS256", rs256phead, testKey.Public(), testsig, testsig, false},
|
||||||
|
{"ES256-MessageSigner", es256phead, testKeyEC.Public(), es256stdsig, es256jwsig, true},
|
||||||
|
{"RS256-MessageSigner", rs256phead, testKey.Public(), testsig, testsig, true},
|
||||||
}
|
}
|
||||||
for _, tc := range tt {
|
for _, tc := range tt {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.alg, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
stdsig, err := base64.RawStdEncoding.DecodeString(tc.stdsig)
|
stdsig, err := base64.RawStdEncoding.DecodeString(tc.stdsig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("couldn't decode test vector: %v", err)
|
t.Errorf("couldn't decode test vector: %v", err)
|
||||||
}
|
}
|
||||||
signer := &customTestSigner{
|
cs := customTestSigner{
|
||||||
sig: stdsig,
|
sig: stdsig,
|
||||||
pub: tc.pub,
|
pub: tc.pub,
|
||||||
}
|
}
|
||||||
|
var signer crypto.Signer = &cs
|
||||||
|
var ms *customTestMessageSigner
|
||||||
|
if tc.msgSigner {
|
||||||
|
ms = &customTestMessageSigner{customTestSigner: cs}
|
||||||
|
signer = ms
|
||||||
|
}
|
||||||
|
|
||||||
b, err := jwsEncodeJSON(claims, signer, noKeyID, "nonce", "url")
|
b, err := jwsEncodeJSON(claims, signer, noKeyID, "nonce", "url")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -427,6 +453,14 @@ func TestJWSEncodeJSONCustom(t *testing.T) {
|
|||||||
if j.Sig != tc.jwsig {
|
if j.Sig != tc.jwsig {
|
||||||
t.Errorf("j.Sig = %q\nwant %q", j.Sig, tc.jwsig)
|
t.Errorf("j.Sig = %q\nwant %q", j.Sig, tc.jwsig)
|
||||||
}
|
}
|
||||||
|
if ms != nil {
|
||||||
|
if want := tc.phead + "." + payload; string(ms.msg) != want {
|
||||||
|
t.Errorf("msg = %q; want %q", ms.msg, want)
|
||||||
|
}
|
||||||
|
if ms.opts != crypto.SHA256 {
|
||||||
|
t.Errorf("opts = %v; want %v", ms.opts, crypto.SHA256)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-9
@@ -37,7 +37,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
// pebbleModVersion is the module version used for Pebble and Pebble's
|
// pebbleModVersion is the module version used for Pebble and Pebble's
|
||||||
// challenge test server. It is ignored if `-pebble-local-dir` is provided.
|
// challenge test server. It is ignored if `-pebble-local-dir` is provided.
|
||||||
pebbleModVersion = "v2.7.0"
|
pebbleModVersion = "v2.10.1"
|
||||||
// startingPort is the first port number used for binding interface
|
// startingPort is the first port number used for binding interface
|
||||||
// addresses. Each call to takeNextPort() will increment a port number
|
// addresses. Each call to takeNextPort() will increment a port number
|
||||||
// starting at this value.
|
// starting at this value.
|
||||||
@@ -382,7 +382,12 @@ func testIssuance(t *testing.T, env *environment, challSrv challengeServer) {
|
|||||||
// Wait for the order to become ready for finalization.
|
// Wait for the order to become ready for finalization.
|
||||||
order, err = client.WaitOrder(ctx, order.URI)
|
order, err = client.WaitOrder(ctx, order.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
|
var orderErr *acme.OrderError
|
||||||
|
if errors.As(err, &orderErr) {
|
||||||
|
t.Fatalf("failed to wait for order %s: %s: %s", orderURL, err, orderErr.Problem)
|
||||||
|
} else {
|
||||||
|
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if order.Status != acme.StatusReady {
|
if order.Status != acme.StatusReady {
|
||||||
t.Fatalf("expected order %s status to be ready, got %v",
|
t.Fatalf("expected order %s status to be ready, got %v",
|
||||||
@@ -415,7 +420,7 @@ func testIssuance(t *testing.T, env *environment, challSrv challengeServer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the order by creating a certificate with our CSR.
|
// Finalize the order by creating a certificate with our CSR.
|
||||||
chain, _, err := client.CreateOrderCert(ctx, order.FinalizeURL, csrDer, true)
|
chain, _, err := client.CreateCertFromOrder(ctx, order, csrDer, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to finalize order %s with finalize URL %s: %v",
|
t.Fatalf("failed to finalize order %s with finalize URL %s: %v",
|
||||||
orderURL, order.FinalizeURL, err)
|
orderURL, order.FinalizeURL, err)
|
||||||
@@ -633,7 +638,7 @@ func startPebbleEnvironment(t *testing.T, config *environmentConfig) environment
|
|||||||
// Note: we specify -defaultIPv6 "" so that no AAAA records are served.
|
// Note: we specify -defaultIPv6 "" so that no AAAA records are served.
|
||||||
// The LUCI CI runners have issues with IPv6 connectivity on localhost.
|
// The LUCI CI runners have issues with IPv6 connectivity on localhost.
|
||||||
spawnServerProcess(t, binDir, "pebble-challtestsrv",
|
spawnServerProcess(t, binDir, "pebble-challtestsrv",
|
||||||
"-dns01", fmt.Sprintf(":%d", config.dnsPort),
|
"-dnsserver", fmt.Sprintf(":%d", config.dnsPort),
|
||||||
"-defaultIPv6", "",
|
"-defaultIPv6", "",
|
||||||
"-management", fmt.Sprintf(":%d", takeNextPort()),
|
"-management", fmt.Sprintf(":%d", takeNextPort()),
|
||||||
"-doh", "",
|
"-doh", "",
|
||||||
@@ -692,14 +697,14 @@ func startPebbleEnvironment(t *testing.T, config *environmentConfig) environment
|
|||||||
func waitForServer(t *testing.T, addr string) {
|
func waitForServer(t *testing.T, addr string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
if conn, err := net.Dial("tcp", addr); err == nil {
|
if conn, err := net.Dial("tcp", addr); err == nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(i*100) * time.Millisecond)
|
time.Sleep(time.Duration(i*100) * time.Millisecond)
|
||||||
}
|
}
|
||||||
t.Fatalf("failed to connect to %q after 10 tries", addr)
|
t.Fatalf("failed to connect to %q after 20 tries", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchModule fetches the module at the given version and returns the directory
|
// fetchModule fetches the module at the given version and returns the directory
|
||||||
@@ -752,7 +757,7 @@ func prepareBinaries(t *testing.T, pebbleDir string) string {
|
|||||||
|
|
||||||
// We don't want to build in the module cache dir, which might not be
|
// We don't want to build in the module cache dir, which might not be
|
||||||
// writable or to pollute the user's clone with binaries if pebbleLocalDir
|
// writable or to pollute the user's clone with binaries if pebbleLocalDir
|
||||||
//is used.
|
// is used.
|
||||||
binDir := t.TempDir()
|
binDir := t.TempDir()
|
||||||
|
|
||||||
build := func(cmd string) {
|
build := func(cmd string) {
|
||||||
@@ -776,14 +781,29 @@ func prepareBinaries(t *testing.T, pebbleDir string) string {
|
|||||||
func spawnServerProcess(t *testing.T, dir string, cmd string, args ...string) {
|
func spawnServerProcess(t *testing.T, dir string, cmd string, args ...string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
|
||||||
cmdInstance := exec.Command("./"+cmd, args...)
|
cmdInstance := exec.Command("./"+cmd, args...)
|
||||||
cmdInstance.Dir = dir
|
cmdInstance.Dir = dir
|
||||||
cmdInstance.Stdout = os.Stdout
|
cmdInstance.Stdout = &stdout
|
||||||
cmdInstance.Stderr = os.Stderr
|
cmdInstance.Stderr = &stderr
|
||||||
|
|
||||||
if err := cmdInstance.Start(); err != nil {
|
if err := cmdInstance.Start(); err != nil {
|
||||||
t.Fatalf("failed to start %s: %v", cmd, err)
|
t.Fatalf("failed to start %s: %v", cmd, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
cmdInstance.Process.Kill()
|
cmdInstance.Process.Kill()
|
||||||
|
cmdInstance.Wait()
|
||||||
|
|
||||||
|
if t.Failed() || testing.Verbose() {
|
||||||
|
t.Logf("=== %s output ===", cmd)
|
||||||
|
if stdout.Len() > 0 {
|
||||||
|
t.Logf("stdout:\n%s", strings.TrimSpace(stdout.String()))
|
||||||
|
}
|
||||||
|
if stderr.Len() > 0 {
|
||||||
|
t.Logf("stderr:\n%s", strings.TrimSpace(stderr.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-8
@@ -53,6 +53,9 @@ func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tos
|
|||||||
Contact: acct.Contact,
|
Contact: acct.Contact,
|
||||||
}
|
}
|
||||||
if c.dir.Terms != "" {
|
if c.dir.Terms != "" {
|
||||||
|
if prompt == nil {
|
||||||
|
return nil, errors.New("acme: missing Manager.Prompt to accept server's terms of service")
|
||||||
|
}
|
||||||
req.TermsAgreed = prompt(c.dir.Terms)
|
req.TermsAgreed = prompt(c.dir.Terms)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +197,7 @@ func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) e
|
|||||||
// identify those with StatusPending status and fulfill a challenge using Accept.
|
// identify those with StatusPending status and fulfill a challenge using Accept.
|
||||||
// Once all authorizations are satisfied, the caller will typically want to poll
|
// Once all authorizations are satisfied, the caller will typically want to poll
|
||||||
// order status using WaitOrder until it's in StatusReady state.
|
// order status using WaitOrder until it's in StatusReady state.
|
||||||
// To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert.
|
// To finalize the order and obtain a certificate, the caller submits a CSR with CreateCertFromOrder.
|
||||||
func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
|
func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
|
||||||
dir, err := c.Discover(ctx)
|
dir, err := c.Discover(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -232,7 +235,7 @@ func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderO
|
|||||||
return responseOrder(res)
|
return responseOrder(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrder retrives an order identified by the given URL.
|
// GetOrder retrieves an order identified by the given URL.
|
||||||
// For orders created with AuthorizeOrder, the url value is Order.URI.
|
// For orders created with AuthorizeOrder, the url value is Order.URI.
|
||||||
//
|
//
|
||||||
// If a caller needs to poll an order until its status is final,
|
// If a caller needs to poll an order until its status is final,
|
||||||
@@ -247,7 +250,11 @@ func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
return responseOrder(res)
|
o, err := responseOrder(res)
|
||||||
|
if err == nil && o.URI == "" {
|
||||||
|
o.URI = url
|
||||||
|
}
|
||||||
|
return o, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitOrder polls an order from the given URL until it is in one of the final states,
|
// WaitOrder polls an order from the given URL until it is in one of the final states,
|
||||||
@@ -268,11 +275,14 @@ func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
|
|||||||
}
|
}
|
||||||
o, err := responseOrder(res)
|
o, err := responseOrder(res)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
|
if err == nil && o.URI == "" {
|
||||||
|
o.URI = url
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
// Skip and retry.
|
// Skip and retry.
|
||||||
case o.Status == StatusInvalid:
|
case o.Status == StatusInvalid:
|
||||||
return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
|
return nil, &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
|
||||||
case o.Status == StatusReady || o.Status == StatusValid:
|
case o.Status == StatusReady || o.Status == StatusValid:
|
||||||
return o, nil
|
return o, nil
|
||||||
}
|
}
|
||||||
@@ -335,11 +345,43 @@ func responseOrder(res *http.Response) (*Order, error) {
|
|||||||
// certificate chain. Otherwise, only a leaf certificate is returned.
|
// certificate chain. Otherwise, only a leaf certificate is returned.
|
||||||
// The returned URL can be used to re-fetch the certificate using FetchCert.
|
// The returned URL can be used to re-fetch the certificate using FetchCert.
|
||||||
//
|
//
|
||||||
// This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs.
|
// This method is only supported by CAs implementing RFC 8555 that return a
|
||||||
|
// Location header for order finalization responses. Most users should prefer
|
||||||
|
// CreateCertFromOrder, an alternative that has better interoperation with RFC 8555
|
||||||
|
// compliant CAs. See CreateCert for pre-RFC CAs.
|
||||||
//
|
//
|
||||||
// CreateOrderCert returns an error if the CA's response is unreasonably large.
|
// CreateOrderCert returns an error if the CA's response is unreasonably large.
|
||||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||||
func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||||
|
return c.createOrderCert(ctx, url, "", csr, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCertFromOrder submits the CSR (Certificate Signing Request) to a CA for
|
||||||
|
// the specified Order created with AuthorizeOrder.
|
||||||
|
//
|
||||||
|
// If the bundle argument is true, the returned value also contain the CA (issuer)
|
||||||
|
// certificate chain. Otherwise, only a leaf certificate is returned.
|
||||||
|
// The returned URL can be used to re-fetch the certificate using FetchCert.
|
||||||
|
//
|
||||||
|
// This method is only supported by CAs implementing RFC 8555.
|
||||||
|
// See CreateCert for pre-RFC CAs.
|
||||||
|
//
|
||||||
|
// CreateCertFromOrder returns an error if the CA's response is unreasonably large.
|
||||||
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||||
|
func (c *Client) CreateCertFromOrder(ctx context.Context, order *Order, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||||
|
if order == nil {
|
||||||
|
return nil, "", errors.New("acme: nil order")
|
||||||
|
}
|
||||||
|
if order.URI == "" {
|
||||||
|
return nil, "", errors.New("acme: order URI is empty")
|
||||||
|
}
|
||||||
|
if order.FinalizeURL == "" {
|
||||||
|
return nil, "", errors.New("acme: order finalize URL is empty")
|
||||||
|
}
|
||||||
|
return c.createOrderCert(ctx, order.FinalizeURL, order.URI, csr, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) createOrderCert(ctx context.Context, finalizeURL, orderURL string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||||
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
|
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@@ -350,7 +392,7 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu
|
|||||||
}{
|
}{
|
||||||
CSR: base64.RawURLEncoding.EncodeToString(csr),
|
CSR: base64.RawURLEncoding.EncodeToString(csr),
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
res, err := c.post(ctx, nil, finalizeURL, req, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@@ -362,14 +404,17 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu
|
|||||||
|
|
||||||
// Wait for CA to issue the cert if they haven't.
|
// Wait for CA to issue the cert if they haven't.
|
||||||
if o.Status != StatusValid {
|
if o.Status != StatusValid {
|
||||||
o, err = c.WaitOrder(ctx, o.URI)
|
if orderURL == "" {
|
||||||
|
orderURL = o.URI
|
||||||
|
}
|
||||||
|
o, err = c.WaitOrder(ctx, orderURL)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
// The only acceptable status post finalize and WaitOrder is "valid".
|
// The only acceptable status post finalize and WaitOrder is "valid".
|
||||||
if o.Status != StatusValid {
|
if o.Status != StatusValid {
|
||||||
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
|
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
|
||||||
}
|
}
|
||||||
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
|
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
|
||||||
return crt, o.CertURL, err
|
return crt, o.CertURL, err
|
||||||
|
|||||||
+90
-3
@@ -885,11 +885,17 @@ func TestRFC_WaitOrderError(t *testing.T) {
|
|||||||
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
|
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Location", s.url("/orders/1"))
|
w.Header().Set("Location", s.url("/orders/1"))
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
s := StatusPending
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
s = StatusInvalid
|
// https://www.rfc-editor.org/rfc/rfc8555#section-7.3.3
|
||||||
|
errorData := `{
|
||||||
|
"type": "urn:ietf:params:acme:error:userActionRequired",
|
||||||
|
"detail": "Terms of service have changed",
|
||||||
|
"instance": "https://example.com/acme/agreement/?token=W8Ih3PswD-8"
|
||||||
|
}`
|
||||||
|
fmt.Fprintf(w, `{"status": %q, "error": %s}`, StatusInvalid, errorData)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `{"status": %q}`, StatusPending)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, `{"status": %q}`, s)
|
|
||||||
count++
|
count++
|
||||||
})
|
})
|
||||||
s.start()
|
s.start()
|
||||||
@@ -910,6 +916,13 @@ func TestRFC_WaitOrderError(t *testing.T) {
|
|||||||
if e.Status != StatusInvalid {
|
if e.Status != StatusInvalid {
|
||||||
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
|
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
|
||||||
}
|
}
|
||||||
|
if e.Problem == nil {
|
||||||
|
t.Errorf("e.Problem = nil")
|
||||||
|
}
|
||||||
|
expectedProbType := "urn:ietf:params:acme:error:userActionRequired"
|
||||||
|
if e.Problem.ProblemType != expectedProbType {
|
||||||
|
t.Errorf("e.Problem.ProblemType = %q; want %q", e.Problem.ProblemType, expectedProbType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRFC_CreateOrderCert(t *testing.T) {
|
func TestRFC_CreateOrderCert(t *testing.T) {
|
||||||
@@ -966,6 +979,80 @@ func TestRFC_CreateOrderCert(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRFC_CreateCertFromOrder tests that finalization responses work even when
|
||||||
|
// they omit a Location header. Such a header isn't required by RFC 8555 in this
|
||||||
|
// context and we should be able to complete an order without needing it.
|
||||||
|
func TestRFC_CreateCertFromOrder(t *testing.T) {
|
||||||
|
q := &x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{CommonName: "example.org"},
|
||||||
|
}
|
||||||
|
csr, err := x509.CreateCertificateRequest(rand.Reader, q, testKeyEC)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := &x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||||
|
leaf, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &testKeyEC.PublicKey, testKeyEC)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := newACMEServer()
|
||||||
|
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Location", s.url("/accounts/1"))
|
||||||
|
w.Write([]byte(`{"status": "valid"}`))
|
||||||
|
})
|
||||||
|
s.handle("/finalize", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, `{"status":"processing"}`)
|
||||||
|
})
|
||||||
|
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, `{"status":"valid", "certificate":%q}`, s.url("/crt"))
|
||||||
|
})
|
||||||
|
s.handle("/crt", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/pem-certificate-chain")
|
||||||
|
pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: leaf})
|
||||||
|
})
|
||||||
|
s.start()
|
||||||
|
defer s.close()
|
||||||
|
|
||||||
|
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
|
||||||
|
order := &Order{URI: s.url("/orders/1"), FinalizeURL: s.url("/finalize")}
|
||||||
|
cert, curl, err := cl.CreateCertFromOrder(context.Background(), order, csr, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateCertFromOrder: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := x509.ParseCertificate(cert[0]); err != nil {
|
||||||
|
t.Errorf("ParseCertificate: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(cert[0], leaf) {
|
||||||
|
t.Errorf("cert and leaf bytes don't match")
|
||||||
|
}
|
||||||
|
if u := s.url("/crt"); curl != u {
|
||||||
|
t.Errorf("curl = %q; want %q", curl, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRFC_CreateCertFromOrderInvalidOrder tests that invalid CreateCertFromOrder
|
||||||
|
// calls fail before making a request.
|
||||||
|
func TestRFC_CreateCertFromOrderInvalidOrder(t *testing.T) {
|
||||||
|
cl := new(Client)
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
order *Order
|
||||||
|
}{
|
||||||
|
{"nil", nil},
|
||||||
|
{"empty URI", &Order{FinalizeURL: "https://example.com/finalize"}},
|
||||||
|
{"empty finalize URL", &Order{URI: "https://example.com/order/1"}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if _, _, err := cl.CreateCertFromOrder(context.Background(), tt.order, nil, false); err == nil {
|
||||||
|
t.Fatal("CreateCertFromOrder returned nil error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRFC_AlreadyRevokedCert(t *testing.T) {
|
func TestRFC_AlreadyRevokedCert(t *testing.T) {
|
||||||
s := newACMEServer()
|
s := newACMEServer()
|
||||||
s.handle("/acme/revoke-cert", func(w http.ResponseWriter, r *http.Request) {
|
s.handle("/acme/revoke-cert", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
+15
-7
@@ -114,14 +114,15 @@ type Error struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
func (e *Error) Error() string {
|
||||||
str := fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail)
|
var sb strings.Builder
|
||||||
|
fmt.Fprintf(&sb, "%d %s: %s", e.StatusCode, e.ProblemType, e.Detail)
|
||||||
if len(e.Subproblems) > 0 {
|
if len(e.Subproblems) > 0 {
|
||||||
str += fmt.Sprintf("; subproblems:")
|
sb.WriteString("; subproblems:")
|
||||||
for _, sp := range e.Subproblems {
|
for _, sp := range e.Subproblems {
|
||||||
str += fmt.Sprintf("\n\t%s", sp)
|
fmt.Fprintf(&sb, "\n\t%s", sp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return str
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizationError indicates that an authorization for an identifier
|
// AuthorizationError indicates that an authorization for an identifier
|
||||||
@@ -154,17 +155,24 @@ func (a *AuthorizationError) Error() string {
|
|||||||
|
|
||||||
// OrderError is returned from Client's order related methods.
|
// OrderError is returned from Client's order related methods.
|
||||||
// It indicates the order is unusable and the clients should start over with
|
// It indicates the order is unusable and the clients should start over with
|
||||||
// AuthorizeOrder.
|
// AuthorizeOrder. A Problem description may be provided with details on
|
||||||
|
// what caused the order to become unusable.
|
||||||
//
|
//
|
||||||
// The clients can still fetch the order object from CA using GetOrder
|
// The clients can still fetch the order object from CA using GetOrder
|
||||||
// to inspect its state.
|
// to inspect its state.
|
||||||
type OrderError struct {
|
type OrderError struct {
|
||||||
OrderURL string
|
OrderURL string
|
||||||
Status string
|
Status string
|
||||||
|
// Problem is the error that occurred while processing the order.
|
||||||
|
Problem *Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oe *OrderError) Error() string {
|
func (oe *OrderError) Error() string {
|
||||||
return fmt.Sprintf("acme: order %s status: %s", oe.OrderURL, oe.Status)
|
str := fmt.Sprintf("acme: order %s status: %s", oe.OrderURL, oe.Status)
|
||||||
|
if oe.Problem != nil {
|
||||||
|
str += fmt.Sprintf("; problem: %s", oe.Problem)
|
||||||
|
}
|
||||||
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
// RateLimit reports whether err represents a rate limit error and
|
// RateLimit reports whether err represents a rate limit error and
|
||||||
@@ -322,7 +330,7 @@ type Order struct {
|
|||||||
// Possible values are StatusPending, StatusReady, StatusProcessing, StatusValid and StatusInvalid.
|
// Possible values are StatusPending, StatusReady, StatusProcessing, StatusValid and StatusInvalid.
|
||||||
// Pending means the CA does not believe that the client has fulfilled the requirements.
|
// Pending means the CA does not believe that the client has fulfilled the requirements.
|
||||||
// Ready indicates that the client has fulfilled all the requirements and can submit a CSR
|
// Ready indicates that the client has fulfilled all the requirements and can submit a CSR
|
||||||
// to obtain a certificate. This is done with Client's CreateOrderCert.
|
// to obtain a certificate. This is done with Client's CreateCertFromOrder.
|
||||||
// Processing means the certificate is being issued.
|
// Processing means the certificate is being issued.
|
||||||
// Valid indicates the CA has issued the certificate. It can be downloaded
|
// Valid indicates the CA has issued the certificate. It can be downloaded
|
||||||
// from the Order's CertURL. This is done with Client's FetchCert.
|
// from the Order's CertURL. This is done with Client's FetchCert.
|
||||||
|
|||||||
@@ -217,3 +217,19 @@ func TestErrorStringerWithSubproblems(t *testing.T) {
|
|||||||
t.Errorf("Unexpected error string: wanted %q, got %q", expectedStr, err.Error())
|
t.Errorf("Unexpected error string: wanted %q, got %q", expectedStr, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOrderErrorProblem(t *testing.T) {
|
||||||
|
oe := &OrderError{
|
||||||
|
OrderURL: "url",
|
||||||
|
Status: "invalid",
|
||||||
|
Problem: &Error{
|
||||||
|
StatusCode: 400,
|
||||||
|
ProblemType: "type",
|
||||||
|
Detail: "detail",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
want := "acme: order url status: invalid; problem: 400 type: detail"
|
||||||
|
if got := oe.Error(); got != want {
|
||||||
|
t.Errorf("oe.Error() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+22
-17
@@ -6,7 +6,7 @@
|
|||||||
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
||||||
// be used to derive cryptographic keys from passwords.
|
// be used to derive cryptographic keys from passwords.
|
||||||
//
|
//
|
||||||
// For a detailed specification of Argon2 see [1].
|
// For a detailed specification of Argon2 see [argon2-specs.pdf].
|
||||||
//
|
//
|
||||||
// If you aren't sure which function you need, use Argon2id (IDKey) and
|
// If you aren't sure which function you need, use Argon2id (IDKey) and
|
||||||
// the parameter recommendations for your scenario.
|
// the parameter recommendations for your scenario.
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
// It uses data-independent memory access, which is preferred for password
|
// It uses data-independent memory access, which is preferred for password
|
||||||
// hashing and password-based key derivation. Argon2i requires more passes over
|
// hashing and password-based key derivation. Argon2i requires more passes over
|
||||||
// memory than Argon2id to protect from trade-off attacks. The recommended
|
// memory than Argon2id to protect from trade-off attacks. The recommended
|
||||||
// parameters (taken from [2]) for non-interactive operations are time=3 and to
|
// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive
|
||||||
// use the maximum available memory.
|
// operations are time=3 and to use the maximum available memory.
|
||||||
//
|
//
|
||||||
// # Argon2id
|
// # Argon2id
|
||||||
//
|
//
|
||||||
@@ -26,12 +26,15 @@
|
|||||||
// Argon2i and Argon2d. It uses data-independent memory access for the first
|
// Argon2i and Argon2d. It uses data-independent memory access for the first
|
||||||
// half of the first iteration over the memory and data-dependent memory access
|
// half of the first iteration over the memory and data-dependent memory access
|
||||||
// for the rest. Argon2id is side-channel resistant and provides better brute-
|
// for the rest. Argon2id is side-channel resistant and provides better brute-
|
||||||
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
|
// force cost savings due to time-memory tradeoffs than Argon2i. [RFC 9106
|
||||||
// parameters for non-interactive operations (taken from [2]) are time=1 and to
|
// Section 4] recommends time=1, memory=2*1024*1024 KiB (2 GiB), and threads=4
|
||||||
// use the maximum available memory.
|
// as the first recommended option. If much less memory is available, it
|
||||||
|
// recommends time=3, memory=64*1024 KiB (64 MiB), and threads=4 as the second
|
||||||
|
// recommended option.
|
||||||
//
|
//
|
||||||
// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
// [argon2-specs.pdf]: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||||
// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
|
// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4
|
||||||
|
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
|
||||||
package argon2
|
package argon2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -59,9 +62,9 @@ const (
|
|||||||
//
|
//
|
||||||
// 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.
|
// The example above uses time=3 and memory=32*1024. Argon2i generally
|
||||||
// If using that amount of memory (32 MB) is not possible in some contexts then
|
// requires more passes over memory than Argon2id. If in doubt, prefer IDKey
|
||||||
// the time parameter can be increased to compensate.
|
// and its Argon2id parameter recommendations.
|
||||||
//
|
//
|
||||||
// The time parameter specifies the number of passes over the memory and the
|
// The time parameter specifies the number of passes over the memory and the
|
||||||
// memory parameter specifies the size of the memory in KiB. For example
|
// memory parameter specifies the size of the memory in KiB. For example
|
||||||
@@ -81,18 +84,20 @@ 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
|
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||||
// 32-byte key) by doing:
|
// 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, 2*1024*1024, 4, 32)
|
||||||
//
|
//
|
||||||
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
|
// The example above uses the first [RFC 9106 Section 4] recommended option.
|
||||||
// If using that amount of memory (64 MB) is not possible in some contexts then
|
// If much less memory is available, the second recommended option is time=3,
|
||||||
// the time parameter can be increased to compensate.
|
// memory=64*1024 KiB (64 MiB), and threads=4.
|
||||||
//
|
//
|
||||||
// The time parameter specifies the number of passes over the memory and the
|
// The time parameter specifies the number of passes over the memory and the
|
||||||
// memory parameter specifies the size of the memory in KiB. For example
|
// memory parameter specifies the size of the memory in KiB. For example
|
||||||
// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
|
// memory=2*1024*1024 sets the memory cost to ~2 GiB. The number of threads can
|
||||||
// adjusted to the numbers of available CPUs. The cost parameters should be
|
// be adjusted to the numbers of available CPUs. The cost parameters should be
|
||||||
// increased as memory latency and CPU parallelism increases. Remember to get a
|
// increased as memory latency and CPU parallelism increases. Remember to get a
|
||||||
// good random salt.
|
// good random salt.
|
||||||
|
//
|
||||||
|
// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4
|
||||||
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||||
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
|
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ var (
|
|||||||
|
|
||||||
func TestArgon2(t *testing.T) {
|
func TestArgon2(t *testing.T) {
|
||||||
defer func(sse4 bool) { useSSE4 = sse4 }(useSSE4)
|
defer func(sse4 bool) { useSSE4 = sse4 }(useSSE4)
|
||||||
|
defer func(lsx bool) { useLSX = lsx }(useLSX)
|
||||||
|
|
||||||
if useSSE4 {
|
if useSSE4 {
|
||||||
t.Log("SSE4.1 version")
|
t.Log("SSE4.1 version")
|
||||||
@@ -32,6 +33,14 @@ func TestArgon2(t *testing.T) {
|
|||||||
testArgon2id(t)
|
testArgon2id(t)
|
||||||
useSSE4 = false
|
useSSE4 = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if useLSX {
|
||||||
|
t.Log("LSX version")
|
||||||
|
testArgon2i(t)
|
||||||
|
testArgon2d(t)
|
||||||
|
testArgon2id(t)
|
||||||
|
useLSX = false
|
||||||
|
}
|
||||||
t.Log("generic version")
|
t.Log("generic version")
|
||||||
testArgon2i(t)
|
testArgon2i(t)
|
||||||
testArgon2d(t)
|
testArgon2d(t)
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ package argon2
|
|||||||
|
|
||||||
var useSSE4 bool
|
var useSSE4 bool
|
||||||
|
|
||||||
|
// useLSX reports whether the loong64 LSX vector implementation should be
|
||||||
|
// used. It is set from cpu.Loong64.HasLSX in blamka_loong64.go's init().
|
||||||
|
var useLSX bool
|
||||||
|
|
||||||
func processBlockGeneric(out, in1, in2 *block, xor bool) {
|
func processBlockGeneric(out, in1, in2 *block, xor bool) {
|
||||||
var t block
|
var t block
|
||||||
for i := range t {
|
for i := range t {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build loong64 && gc && !purego
|
||||||
|
|
||||||
|
package argon2
|
||||||
|
|
||||||
|
import "golang.org/x/sys/cpu"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// useLSX (declared in blamka_generic.go) gates the LSX vector path.
|
||||||
|
// If the running CPU lacks LSX support, processBlockVX falls back
|
||||||
|
// to processBlockGeneric.
|
||||||
|
useLSX = cpu.Loong64.HasLSX
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func blamkaLOONG64(b *block)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func mixBlocksVX(out, a, b, c *block)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func xorBlocksVX(out, a, b, c *block)
|
||||||
|
|
||||||
|
func processBlock(out, in1, in2 *block) {
|
||||||
|
processBlockVX(out, in1, in2, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockXOR(out, in1, in2 *block) {
|
||||||
|
processBlockVX(out, in1, in2, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockVX(out, in1, in2 *block, xor bool) {
|
||||||
|
if !useLSX {
|
||||||
|
processBlockGeneric(out, in1, in2, xor)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var t block
|
||||||
|
mixBlocksVX(&t, in1, in2, &t)
|
||||||
|
blamkaLOONG64(&t)
|
||||||
|
if xor {
|
||||||
|
xorBlocksVX(out, in1, in2, &t)
|
||||||
|
} else {
|
||||||
|
mixBlocksVX(out, in1, in2, &t)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build loong64 && gc && !purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// HALF_ROUND applies one BlaMka half-round (mul-add-xor-rotate) to two
|
||||||
|
// groups of 4 columns, each packed 2-wide into V registers (v0/v2/v4/v6
|
||||||
|
// hold one pair of columns in lane0/lane1, v1/v3/v5/v7 hold the other).
|
||||||
|
// t0, t1 are scratch registers, one per independent column group.
|
||||||
|
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, t1) \
|
||||||
|
VMULWEVVWU v0, v2, t0; \
|
||||||
|
VADDV t0, t0, t0; \
|
||||||
|
VADDV v0, v2, v0; \
|
||||||
|
VADDV v0, t0, v0; \
|
||||||
|
VXORV v6, v0, v6; \
|
||||||
|
VROTRV $32, v6, v6; \
|
||||||
|
VMULWEVVWU v1, v3, t1; \
|
||||||
|
VADDV t1, t1, t1; \
|
||||||
|
VADDV v1, v3, v1; \
|
||||||
|
VADDV v1, t1, v1; \
|
||||||
|
VXORV v7, v1, v7; \
|
||||||
|
VROTRV $32, v7, v7; \
|
||||||
|
VMULWEVVWU v4, v6, t0; \
|
||||||
|
VADDV t0, t0, t0; \
|
||||||
|
VADDV v4, v6, v4; \
|
||||||
|
VADDV v4, t0, v4; \
|
||||||
|
VXORV v2, v4, v2; \
|
||||||
|
VROTRV $24, v2, v2; \
|
||||||
|
VMULWEVVWU v5, v7, t1; \
|
||||||
|
VADDV t1, t1, t1; \
|
||||||
|
VADDV v5, v7, v5; \
|
||||||
|
VADDV v5, t1, v5; \
|
||||||
|
VXORV v3, v5, v3; \
|
||||||
|
VROTRV $24, v3, v3; \
|
||||||
|
VMULWEVVWU v0, v2, t0; \
|
||||||
|
VADDV t0, t0, t0; \
|
||||||
|
VADDV v0, v2, v0; \
|
||||||
|
VADDV v0, t0, v0; \
|
||||||
|
VXORV v6, v0, v6; \
|
||||||
|
VROTRV $16, v6, v6; \
|
||||||
|
VMULWEVVWU v1, v3, t1; \
|
||||||
|
VADDV t1, t1, t1; \
|
||||||
|
VADDV v1, v3, v1; \
|
||||||
|
VADDV v1, t1, v1; \
|
||||||
|
VXORV v7, v1, v7; \
|
||||||
|
VROTRV $16, v7, v7; \
|
||||||
|
VMULWEVVWU v4, v6, t0; \
|
||||||
|
VADDV t0, t0, t0; \
|
||||||
|
VADDV v4, v6, v4; \
|
||||||
|
VADDV v4, t0, v4; \
|
||||||
|
VXORV v2, v4, v2; \
|
||||||
|
VROTRV $63, v2, v2; \
|
||||||
|
VMULWEVVWU v5, v7, t1; \
|
||||||
|
VADDV t1, t1, t1; \
|
||||||
|
VADDV v5, v7, v5; \
|
||||||
|
VADDV v5, t1, v5; \
|
||||||
|
VXORV v3, v5, v3; \
|
||||||
|
VROTRV $63, v3, v3
|
||||||
|
|
||||||
|
// SHUFFLE / SHUFFLE_INV: reused verbatim from blake2b_loong64.s -- amd64
|
||||||
|
// avo source confirms argon2's SHUFFLE/SHUFFLE_INV and blake2b's SHUFFLE
|
||||||
|
// are byte-for-byte identical macros (diagonalize / undiagonalize),
|
||||||
|
// operating only on V2..V7 (V0/V1 untouched), with V13 held at zero.
|
||||||
|
#define SHUFFLE \
|
||||||
|
VADDV V2, V13, V12; \
|
||||||
|
VADDV V6, V13, V16; \
|
||||||
|
VADDV V4, V13, V14; \
|
||||||
|
VADDV V5, V13, V4; \
|
||||||
|
VADDV V14, V13, V5; \
|
||||||
|
VSHUF4IV $9, V3, V2; \
|
||||||
|
VSHUF4IV $9, V12, V3; \
|
||||||
|
VSHUF4IV $3, V7, V6; \
|
||||||
|
VSHUF4IV $3, V16, V7
|
||||||
|
|
||||||
|
#define SHUFFLE_INV \
|
||||||
|
VADDV V2, V13, V12; \
|
||||||
|
VADDV V6, V13, V16; \
|
||||||
|
VADDV V4, V13, V14; \
|
||||||
|
VADDV V5, V13, V4; \
|
||||||
|
VADDV V14, V13, V5; \
|
||||||
|
VSHUF4IV $3, V3, V2; \
|
||||||
|
VSHUF4IV $3, V12, V3; \
|
||||||
|
VSHUF4IV $9, V7, V6; \
|
||||||
|
VSHUF4IV $9, V16, V7
|
||||||
|
|
||||||
|
// LOAD_MSG_0 / STORE_MSG_0: row pass. `off` is the byte offset of the
|
||||||
|
// 128-byte (16-uint64) row group; within the macro the 8 loads step by
|
||||||
|
// 16B (2 uint64 per V register). Called from blamkaLOONG64 with off
|
||||||
|
// stepping by 128B across the 8 row groups -- do not confuse the two
|
||||||
|
// strides.
|
||||||
|
#define LOAD_MSG_0(off) \
|
||||||
|
VMOVQ (off+0)(R4), V0; \
|
||||||
|
VMOVQ (off+16)(R4), V1; \
|
||||||
|
VMOVQ (off+32)(R4), V2; \
|
||||||
|
VMOVQ (off+48)(R4), V3; \
|
||||||
|
VMOVQ (off+64)(R4), V4; \
|
||||||
|
VMOVQ (off+80)(R4), V5; \
|
||||||
|
VMOVQ (off+96)(R4), V6; \
|
||||||
|
VMOVQ (off+112)(R4), V7
|
||||||
|
|
||||||
|
#define STORE_MSG_0(off) \
|
||||||
|
VMOVQ V0, (off+0)(R4); \
|
||||||
|
VMOVQ V1, (off+16)(R4); \
|
||||||
|
VMOVQ V2, (off+32)(R4); \
|
||||||
|
VMOVQ V3, (off+48)(R4); \
|
||||||
|
VMOVQ V4, (off+64)(R4); \
|
||||||
|
VMOVQ V5, (off+80)(R4); \
|
||||||
|
VMOVQ V6, (off+96)(R4); \
|
||||||
|
VMOVQ V7, (off+112)(R4)
|
||||||
|
|
||||||
|
// LOAD_MSG_1 / STORE_MSG_1: diagonal pass. `off` is the byte offset within
|
||||||
|
// a 128-byte row group; within the macro the 8 loads step by 128B (one
|
||||||
|
// element from each of the 8 row groups, mirroring blamka_generic.go's
|
||||||
|
// t[off], t[128+off], t[256+off], ...). Called from blamkaLOONG64 with
|
||||||
|
// off stepping by 16B (2 uint64) across the 8 diagonal groups -- the
|
||||||
|
// inner/outer strides are swapped relative to LOAD_MSG_0/STORE_MSG_0.
|
||||||
|
#define LOAD_MSG_1(off) \
|
||||||
|
VMOVQ (off+0)(R4), V0; \
|
||||||
|
VMOVQ (off+128)(R4), V1; \
|
||||||
|
VMOVQ (off+256)(R4), V2; \
|
||||||
|
VMOVQ (off+384)(R4), V3; \
|
||||||
|
VMOVQ (off+512)(R4), V4; \
|
||||||
|
VMOVQ (off+640)(R4), V5; \
|
||||||
|
VMOVQ (off+768)(R4), V6; \
|
||||||
|
VMOVQ (off+896)(R4), V7
|
||||||
|
|
||||||
|
#define STORE_MSG_1(off) \
|
||||||
|
VMOVQ V0, (off+0)(R4); \
|
||||||
|
VMOVQ V1, (off+128)(R4); \
|
||||||
|
VMOVQ V2, (off+256)(R4); \
|
||||||
|
VMOVQ V3, (off+384)(R4); \
|
||||||
|
VMOVQ V4, (off+512)(R4); \
|
||||||
|
VMOVQ V5, (off+640)(R4); \
|
||||||
|
VMOVQ V6, (off+768)(R4); \
|
||||||
|
VMOVQ V7, (off+896)(R4)
|
||||||
|
|
||||||
|
// BLAMKA_ROUND_0(off): one full row pass over the 16-uint64 group at
|
||||||
|
// byte offset off -- corresponds to a single call to blamkaGeneric
|
||||||
|
// with the "row" argument order (t[off]..t[off+15]).
|
||||||
|
#define BLAMKA_ROUND_0(off) \
|
||||||
|
LOAD_MSG_0(off); \
|
||||||
|
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||||
|
SHUFFLE; \
|
||||||
|
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||||
|
SHUFFLE_INV; \
|
||||||
|
STORE_MSG_0(off)
|
||||||
|
|
||||||
|
// BLAMKA_ROUND_1(off): one full diagonal pass over the 16 uint64s at
|
||||||
|
// diagonal offset off -- corresponds to a single call to blamkaGeneric
|
||||||
|
// with the "diagonal" argument order (t[off], t[off+1], t[16+off], ...).
|
||||||
|
#define BLAMKA_ROUND_1(off) \
|
||||||
|
LOAD_MSG_1(off); \
|
||||||
|
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||||
|
SHUFFLE; \
|
||||||
|
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
|
||||||
|
SHUFFLE_INV; \
|
||||||
|
STORE_MSG_1(off)
|
||||||
|
|
||||||
|
// func blamkaLOONG64(b *block)
|
||||||
|
TEXT ·blamkaLOONG64(SB), NOSPLIT, $0-8
|
||||||
|
MOVV b+0(FP), R4
|
||||||
|
|
||||||
|
VXORV V13, V13, V13 // V13 = 0, used by SHUFFLE/SHUFFLE_INV
|
||||||
|
|
||||||
|
// Round 0: 8 row passes, stride 128 bytes (16 uint64s).
|
||||||
|
BLAMKA_ROUND_0(0)
|
||||||
|
BLAMKA_ROUND_0(128)
|
||||||
|
BLAMKA_ROUND_0(256)
|
||||||
|
BLAMKA_ROUND_0(384)
|
||||||
|
BLAMKA_ROUND_0(512)
|
||||||
|
BLAMKA_ROUND_0(640)
|
||||||
|
BLAMKA_ROUND_0(768)
|
||||||
|
BLAMKA_ROUND_0(896)
|
||||||
|
|
||||||
|
// Round 1: 8 diagonal passes, stride 16 bytes (2 uint64s).
|
||||||
|
BLAMKA_ROUND_1(0)
|
||||||
|
BLAMKA_ROUND_1(16)
|
||||||
|
BLAMKA_ROUND_1(32)
|
||||||
|
BLAMKA_ROUND_1(48)
|
||||||
|
BLAMKA_ROUND_1(64)
|
||||||
|
BLAMKA_ROUND_1(80)
|
||||||
|
BLAMKA_ROUND_1(96)
|
||||||
|
BLAMKA_ROUND_1(112)
|
||||||
|
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func mixBlocksVX(out, a, b, c *block)
|
||||||
|
TEXT ·mixBlocksVX(SB), NOSPLIT, $0-32
|
||||||
|
MOVV out+0(FP), R4
|
||||||
|
MOVV a+8(FP), R5
|
||||||
|
MOVV b+16(FP), R6
|
||||||
|
MOVV c+24(FP), R7
|
||||||
|
MOVV $1024, R8
|
||||||
|
|
||||||
|
PCALIGN $16
|
||||||
|
mixloop:
|
||||||
|
VMOVQ (R5), V0
|
||||||
|
VMOVQ (R6), V1
|
||||||
|
VMOVQ (R7), V2
|
||||||
|
VXORV V0, V1, V0
|
||||||
|
VXORV V0, V2, V0
|
||||||
|
VMOVQ V0, (R4)
|
||||||
|
ADDV $16, R4
|
||||||
|
ADDV $16, R5
|
||||||
|
ADDV $16, R6
|
||||||
|
ADDV $16, R7
|
||||||
|
SUBV $16, R8
|
||||||
|
BNE R8, R0, mixloop
|
||||||
|
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func xorBlocksVX(out, a, b, c *block)
|
||||||
|
TEXT ·xorBlocksVX(SB), NOSPLIT, $0-32
|
||||||
|
MOVV out+0(FP), R4
|
||||||
|
MOVV a+8(FP), R5
|
||||||
|
MOVV b+16(FP), R6
|
||||||
|
MOVV c+24(FP), R7
|
||||||
|
MOVV $1024, R8
|
||||||
|
|
||||||
|
PCALIGN $16
|
||||||
|
xorloop:
|
||||||
|
VMOVQ (R5), V0
|
||||||
|
VMOVQ (R6), V1
|
||||||
|
VMOVQ (R7), V2
|
||||||
|
VMOVQ (R4), V3
|
||||||
|
VXORV V0, V1, V0
|
||||||
|
VXORV V0, V2, V0
|
||||||
|
VXORV V0, V3, V0
|
||||||
|
VMOVQ V0, (R4)
|
||||||
|
ADDV $16, R4
|
||||||
|
ADDV $16, R5
|
||||||
|
ADDV $16, R6
|
||||||
|
ADDV $16, R7
|
||||||
|
SUBV $16, R8
|
||||||
|
BNE R8, R0, xorloop
|
||||||
|
|
||||||
|
RET
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || purego || !gc
|
//go:build (!amd64 && !loong64) || purego || !gc
|
||||||
|
|
||||||
package argon2
|
package argon2
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build loong64 && gc && !purego
|
||||||
|
|
||||||
|
package blake2b
|
||||||
|
|
||||||
|
import "golang.org/x/sys/cpu"
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func hashBlocksVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
|
||||||
|
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||||
|
if cpu.Loong64.HasLSX {
|
||||||
|
hashBlocksVX(h, c, flag, blocks)
|
||||||
|
} else {
|
||||||
|
hashBlocksGeneric(h, c, flag, blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build loong64 && gc && !purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA ·iv0<>+0(SB)/8, $0x6a09e667f3bcc908
|
||||||
|
DATA ·iv0<>+8(SB)/8, $0xbb67ae8584caa73b
|
||||||
|
GLOBL ·iv0<>(SB), RODATA|NOPTR, $16
|
||||||
|
|
||||||
|
DATA ·iv1<>+0(SB)/8, $0x3c6ef372fe94f82b
|
||||||
|
DATA ·iv1<>+8(SB)/8, $0xa54ff53a5f1d36f1
|
||||||
|
GLOBL ·iv1<>(SB), RODATA|NOPTR, $16
|
||||||
|
|
||||||
|
DATA ·iv2<>+0(SB)/8, $0x510e527fade682d1
|
||||||
|
DATA ·iv2<>+8(SB)/8, $0x9b05688c2b3e6c1f
|
||||||
|
GLOBL ·iv2<>(SB), RODATA|NOPTR, $16
|
||||||
|
|
||||||
|
DATA ·iv3<>+0(SB)/8, $0x1f83d9abfb41bd6b
|
||||||
|
DATA ·iv3<>+8(SB)/8, $0x5be0cd19137e2179
|
||||||
|
GLOBL ·iv3<>(SB), RODATA|NOPTR, $16
|
||||||
|
|
||||||
|
#define SHUFFLE_1 \
|
||||||
|
VADDV V2, V13, V12; \ // V12 = V2
|
||||||
|
VADDV V6, V13, V16; \ // V16 = V6
|
||||||
|
VADDV V4, V13, V14; \ // V14 = V4
|
||||||
|
VADDV V5, V13, V4; \ // V4 = V5
|
||||||
|
VADDV V14, V13, V5; \ // V5 = V4
|
||||||
|
VSHUF4IV $9, V3, V2; \
|
||||||
|
VSHUF4IV $9, V12, V3; \
|
||||||
|
VSHUF4IV $3, V7, V6; \
|
||||||
|
VSHUF4IV $3, V16, V7; \
|
||||||
|
|
||||||
|
#define SHUFFLE_2 \
|
||||||
|
VADDV V2, V13, V12; \ // V12 = V2
|
||||||
|
VADDV V6, V13, V16; \ // V16 = V6
|
||||||
|
VADDV V4, V13, V14; \ // V14 = V4
|
||||||
|
VADDV V5, V13, V4; \ // V4 = V5
|
||||||
|
VADDV V14, V13, V5; \ // V5 = V4
|
||||||
|
VSHUF4IV $3, V3, V2; \
|
||||||
|
VSHUF4IV $3, V12, V3; \
|
||||||
|
VSHUF4IV $9, V7, V6; \
|
||||||
|
VSHUF4IV $9, V16, V7; \
|
||||||
|
|
||||||
|
#define LOAD_M(a, b, c, d, e, f, g, h) \
|
||||||
|
VMOVQ a, V8.V[0]; \
|
||||||
|
VMOVQ b, V8.V[1]; \
|
||||||
|
VMOVQ c, V9.V[0]; \
|
||||||
|
VMOVQ d, V9.V[1]; \
|
||||||
|
VMOVQ e, V10.V[0]; \
|
||||||
|
VMOVQ f, V10.V[1]; \
|
||||||
|
VMOVQ g, V11.V[0]; \
|
||||||
|
VMOVQ h, V11.V[1]; \
|
||||||
|
|
||||||
|
#define ROUND_0 \
|
||||||
|
VADDV V0, V8, V0; \
|
||||||
|
VADDV V1, V9, V1; \
|
||||||
|
VADDV V0, V2, V0; \
|
||||||
|
VADDV V1, V3, V1; \
|
||||||
|
VXORV V6, V0, V6; \
|
||||||
|
VXORV V7, V1, V7; \
|
||||||
|
VROTRV $32, V6, V6; \
|
||||||
|
VROTRV $32, V7, V7; \
|
||||||
|
VADDV V4, V6, V4; \
|
||||||
|
VADDV V5, V7, V5; \
|
||||||
|
VXORV V2, V4, V2; \
|
||||||
|
VXORV V3, V5, V3; \
|
||||||
|
VROTRV $24, V2, V2; \
|
||||||
|
VROTRV $24, V3, V3; \
|
||||||
|
VADDV V0, V10, V0; \
|
||||||
|
VADDV V1, V11, V1; \
|
||||||
|
VADDV V0, V2, V0; \
|
||||||
|
VADDV V1, V3, V1; \
|
||||||
|
VXORV V6, V0, V6; \
|
||||||
|
VXORV V7, V1, V7; \
|
||||||
|
VROTRV $16, V6, V6; \
|
||||||
|
VROTRV $16, V7, V7; \
|
||||||
|
VADDV V4, V6, V4; \
|
||||||
|
VADDV V5, V7, V5; \
|
||||||
|
VXORV V2, V4, V2; \
|
||||||
|
VXORV V3, V5, V3; \
|
||||||
|
VROTRV $63, V2, V2; \
|
||||||
|
VROTRV $63, V3, V3; \
|
||||||
|
|
||||||
|
#define ROUND_8 ROUND_0
|
||||||
|
|
||||||
|
// func hashBlocksVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||||
|
TEXT ·hashBlocksVX(SB), NOSPLIT, $0-48
|
||||||
|
MOVV h+0(FP), R4
|
||||||
|
MOVV c+8(FP), R5
|
||||||
|
MOVV flag+16(FP), R6
|
||||||
|
MOVV blocks_base+24(FP), R7
|
||||||
|
MOVV blocks_len+32(FP), R8
|
||||||
|
MOVV (R5), R9 // c0
|
||||||
|
MOVV 8(R5), R10 // c1
|
||||||
|
|
||||||
|
loop:
|
||||||
|
ADDV $0x80, R9
|
||||||
|
SGTU $0x80, R9, R11
|
||||||
|
ADDV R10, R11, R10
|
||||||
|
VMOVQ R9, V8.V[0]
|
||||||
|
VMOVQ R10, V8.V[1]
|
||||||
|
|
||||||
|
MOVV $·iv0<>(SB), R11
|
||||||
|
MOVV $·iv1<>(SB), R12
|
||||||
|
MOVV $·iv2<>(SB), R13
|
||||||
|
MOVV $·iv3<>(SB), R14
|
||||||
|
VMOVQ (R13), V6
|
||||||
|
VMOVQ (R11), V4
|
||||||
|
VMOVQ (R12), V5
|
||||||
|
MOVV (R14), R15 // v14
|
||||||
|
MOVV 8(R14), R16
|
||||||
|
VXORV V6, V8, V6
|
||||||
|
XOR R15, R6, R15
|
||||||
|
VMOVQ R16, V7.V[1]
|
||||||
|
VMOVQ R15, V7.V[0]
|
||||||
|
|
||||||
|
VMOVQ (R4), V0
|
||||||
|
VMOVQ 16(R4), V1
|
||||||
|
VMOVQ 32(R4), V2
|
||||||
|
VMOVQ 48(R4), V3
|
||||||
|
|
||||||
|
MOVV 0(R7), R11
|
||||||
|
MOVV 8(R7), R12
|
||||||
|
MOVV 16(R7), R13
|
||||||
|
MOVV 24(R7), R14
|
||||||
|
MOVV 32(R7), R15
|
||||||
|
MOVV 40(R7), R16
|
||||||
|
MOVV 48(R7), R17
|
||||||
|
MOVV 56(R7), R18
|
||||||
|
MOVV 64(R7), R19
|
||||||
|
MOVV 72(R7), R24
|
||||||
|
MOVV 80(R7), R25
|
||||||
|
MOVV 88(R7), R26
|
||||||
|
MOVV 96(R7), R27
|
||||||
|
MOVV 104(R7), R28
|
||||||
|
MOVV 112(R7), R29
|
||||||
|
MOVV 120(R7), R30
|
||||||
|
|
||||||
|
VXORV V13, V13, V13 // V13=0
|
||||||
|
|
||||||
|
LOAD_M(R11, R13, R15, R17, R12, R14, R16, R18)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R19, R25, R27, R29, R24, R26, R28, R30)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R29, R15, R24, R28, R25, R19, R30, R17)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R12, R11, R26, R16, R27, R13, R18, R14)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R26, R27, R16, R30, R19, R11, R13, R28)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R25, R14, R18, R24, R29, R17, R12, R15)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R18, R14, R28, R26, R24, R12, R27, R29)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R13, R16, R15, R30, R17, R25, R11, R19)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R24, R16, R13, R25, R11, R18, R15, R30)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R29, R26, R17, R14, R12, R27, R19, R28)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R13, R17, R11, R19, R27, R25, R26, R14)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R15, R18, R30, R12, R28, R16, R29, R24)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R27, R12, R29, R15, R16, R30, R28, R25)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R11, R17, R24, R19, R18, R14, R13, R26)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R28, R18, R27, R14, R26, R29, R12, R24)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R16, R30, R19, R13, R11, R15, R17, R25)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R17, R29, R26, R11, R30, R24, R14, R19)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R27, R28, R12, R25, R13, R18, R15, R16)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R25, R19, R18, R12, R13, R15, R17, R16)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R30, R24, R14, R28, R26, R29, R27, R11)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R11, R13, R15, R17, R12, R14, R16, R18)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R19, R25, R27, R29, R24, R26, R28, R30)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
LOAD_M(R29, R15, R24, R28, R25, R19, R30, R17)
|
||||||
|
ROUND_0
|
||||||
|
SHUFFLE_1
|
||||||
|
LOAD_M(R12, R11, R26, R16, R27, R13, R18, R14)
|
||||||
|
ROUND_8
|
||||||
|
SHUFFLE_2
|
||||||
|
|
||||||
|
VMOVQ (R4), V8
|
||||||
|
VMOVQ 16(R4), V9
|
||||||
|
VMOVQ 32(R4), V10
|
||||||
|
VMOVQ 48(R4), V11
|
||||||
|
VXORV V0, V4, V0
|
||||||
|
VXORV V1, V5, V1
|
||||||
|
VXORV V2, V6, V2
|
||||||
|
VXORV V3, V7, V3
|
||||||
|
VXORV V0, V8, V0
|
||||||
|
VXORV V1, V9, V1
|
||||||
|
VXORV V2, V10, V2
|
||||||
|
VXORV V3, V11, V3
|
||||||
|
VMOVQ V0, (R4)
|
||||||
|
VMOVQ V1, 16(R4)
|
||||||
|
VMOVQ V2, 32(R4)
|
||||||
|
VMOVQ V3, 48(R4)
|
||||||
|
|
||||||
|
SUBV $128, R8
|
||||||
|
ADDV $128, R7
|
||||||
|
BNE R8, R0, loop
|
||||||
|
|
||||||
|
MOVV R9, (R5)
|
||||||
|
MOVV R10, 8(R5)
|
||||||
|
|
||||||
|
RET
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || purego || !gc
|
//go:build (!amd64 && !loong64) || purego || !gc
|
||||||
|
|
||||||
package blake2b
|
package blake2b
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ hash.XOF = (*xof)(nil)
|
||||||
|
|
||||||
func fromHex(s string) []byte {
|
func fromHex(s string) []byte {
|
||||||
b, err := hex.DecodeString(s)
|
b, err := hex.DecodeString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ loop:
|
|||||||
MOVD $NUM_ROUNDS, R21
|
MOVD $NUM_ROUNDS, R21
|
||||||
VLD1 (R11), [V30.S4, V31.S4]
|
VLD1 (R11), [V30.S4, V31.S4]
|
||||||
|
|
||||||
// load contants
|
// load constants
|
||||||
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4]
|
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4]
|
||||||
WORD $0x4D60E940
|
WORD $0x4D60E940
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gc && !purego
|
||||||
|
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
import "golang.org/x/sys/cpu"
|
||||||
|
|
||||||
|
const bufSize = 256
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||||
|
|
||||||
|
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||||
|
if cpu.Loong64.HasLSX {
|
||||||
|
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||||
|
} else {
|
||||||
|
c.xorKeyStreamBlocksGeneric(dst, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// derived from chacha_arm64.s
|
||||||
|
|
||||||
|
//go:build gc && !purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA ·constants+0x00(SB)/4, $0x61707865
|
||||||
|
DATA ·constants+0x04(SB)/4, $0x3320646e
|
||||||
|
DATA ·constants+0x08(SB)/4, $0x79622d32
|
||||||
|
DATA ·constants+0x0c(SB)/4, $0x6b206574
|
||||||
|
GLOBL ·constants(SB), NOPTR|RODATA, $32
|
||||||
|
|
||||||
|
DATA ·incRotMatrix+0x00(SB)/4, $0x00000000
|
||||||
|
DATA ·incRotMatrix+0x04(SB)/4, $0x00000001
|
||||||
|
DATA ·incRotMatrix+0x08(SB)/4, $0x00000002
|
||||||
|
DATA ·incRotMatrix+0x0c(SB)/4, $0x00000003
|
||||||
|
GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32
|
||||||
|
|
||||||
|
#define NUM_ROUNDS 10
|
||||||
|
|
||||||
|
// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||||
|
TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0
|
||||||
|
MOVV dst+0(FP), R4
|
||||||
|
MOVV src+24(FP), R5
|
||||||
|
MOVV src_len+32(FP), R6
|
||||||
|
MOVV key+48(FP), R7
|
||||||
|
MOVV nonce+56(FP), R8
|
||||||
|
MOVV counter+64(FP), R9
|
||||||
|
|
||||||
|
MOVV $·constants(SB), R10
|
||||||
|
MOVV $·incRotMatrix(SB), R11
|
||||||
|
|
||||||
|
MOVW (R9), R12
|
||||||
|
|
||||||
|
loop:
|
||||||
|
MOVV $NUM_ROUNDS, R15
|
||||||
|
// load 4-32bit data from incRotMatrix added to counter
|
||||||
|
VMOVQ (R11), V30
|
||||||
|
|
||||||
|
// load contants
|
||||||
|
VMOVQ (R10), V0.W4
|
||||||
|
VMOVQ 4(R10), V1.W4
|
||||||
|
VMOVQ 8(R10), V2.W4
|
||||||
|
VMOVQ 12(R10), V3.W4
|
||||||
|
|
||||||
|
// load keys
|
||||||
|
VMOVQ (R7), V4.W4
|
||||||
|
VMOVQ 4(R7), V5.W4
|
||||||
|
VMOVQ 8(R7), V6.W4
|
||||||
|
VMOVQ 12(R7), V7.W4
|
||||||
|
VMOVQ 16(R7), V8.W4
|
||||||
|
VMOVQ 20(R7), V9.W4
|
||||||
|
VMOVQ 24(R7), V10.W4
|
||||||
|
VMOVQ 28(R7), V11.W4
|
||||||
|
|
||||||
|
// load counter + nonce
|
||||||
|
VMOVQ (R9), V12.W4
|
||||||
|
|
||||||
|
VMOVQ (R8), V13.W4
|
||||||
|
VMOVQ 4(R8), V14.W4
|
||||||
|
VMOVQ 8(R8), V15.W4
|
||||||
|
|
||||||
|
// update counter
|
||||||
|
VADDW V30, V12, V12
|
||||||
|
|
||||||
|
chacha:
|
||||||
|
// V0..V3 += V4..V7
|
||||||
|
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 16)
|
||||||
|
VADDW V0, V4, V0
|
||||||
|
VADDW V1, V5, V1
|
||||||
|
VADDW V2, V6, V2
|
||||||
|
VADDW V3, V7, V3
|
||||||
|
VXORV V12, V0, V12
|
||||||
|
VXORV V13, V1, V13
|
||||||
|
VXORV V14, V2, V14
|
||||||
|
VXORV V15, V3, V15
|
||||||
|
VROTRW $16, V12, V12
|
||||||
|
VROTRW $16, V13, V13
|
||||||
|
VROTRW $16, V14, V14
|
||||||
|
VROTRW $16, V15, V15
|
||||||
|
|
||||||
|
// V8..V11 += V12..V15
|
||||||
|
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 12)
|
||||||
|
VADDW V8, V12, V8
|
||||||
|
VADDW V9, V13, V9
|
||||||
|
VADDW V10, V14, V10
|
||||||
|
VADDW V11, V15, V11
|
||||||
|
VXORV V4, V8, V4
|
||||||
|
VXORV V5, V9, V5
|
||||||
|
VXORV V6, V10, V6
|
||||||
|
VXORV V7, V11, V7
|
||||||
|
VROTRW $20, V4, V4
|
||||||
|
VROTRW $20, V5, V5
|
||||||
|
VROTRW $20, V6, V6
|
||||||
|
VROTRW $20, V7, V7
|
||||||
|
|
||||||
|
// V0..V3 += V4..V7
|
||||||
|
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 8)
|
||||||
|
VADDW V0, V4, V0
|
||||||
|
VADDW V1, V5, V1
|
||||||
|
VADDW V2, V6, V2
|
||||||
|
VADDW V3, V7, V3
|
||||||
|
VXORV V12, V0, V12
|
||||||
|
VXORV V13, V1, V13
|
||||||
|
VXORV V14, V2, V14
|
||||||
|
VXORV V15, V3, V15
|
||||||
|
VROTRW $24, V12, V12
|
||||||
|
VROTRW $24, V13, V13
|
||||||
|
VROTRW $24, V14, V14
|
||||||
|
VROTRW $24, V15, V15
|
||||||
|
|
||||||
|
// V8..V11 += V12..V15
|
||||||
|
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 7)
|
||||||
|
VADDW V12, V8, V8
|
||||||
|
VADDW V13, V9, V9
|
||||||
|
VADDW V14, V10, V10
|
||||||
|
VADDW V15, V11, V11
|
||||||
|
VXORV V4, V8, V4
|
||||||
|
VXORV V5, V9, V5
|
||||||
|
VXORV V6, V10, V6
|
||||||
|
VXORV V7, V11, V7
|
||||||
|
VROTRW $25, V4, V4
|
||||||
|
VROTRW $25, V5, V5
|
||||||
|
VROTRW $25, V6, V6
|
||||||
|
VROTRW $25, V7, V7
|
||||||
|
|
||||||
|
// V0..V3 += V5..V7, V4
|
||||||
|
// V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16)
|
||||||
|
VADDW V0, V5, V0
|
||||||
|
VADDW V1, V6, V1
|
||||||
|
VADDW V2, V7, V2
|
||||||
|
VADDW V3, V4, V3
|
||||||
|
VXORV V15, V0, V15
|
||||||
|
VXORV V12, V1, V12
|
||||||
|
VXORV V13, V2, V13
|
||||||
|
VXORV V14, V3, V14
|
||||||
|
VROTRW $16, V15, V15
|
||||||
|
VROTRW $16, V12, V12
|
||||||
|
VROTRW $16, V13, V13
|
||||||
|
VROTRW $16, V14, V14
|
||||||
|
|
||||||
|
// V10,V11,V8,V9 += V15,V12,V13,V14
|
||||||
|
// V5,V6,V7,V4 <<<= ((V5,V6,V7,V4 XOR V10,V11,V8,V9), 12)
|
||||||
|
VADDW V10, V15, V10
|
||||||
|
VADDW V11, V12, V11
|
||||||
|
VADDW V8, V13, V8
|
||||||
|
VADDW V9, V14, V9
|
||||||
|
VXORV V5, V10, V5
|
||||||
|
VXORV V6, V11, V6
|
||||||
|
VXORV V7, V8, V7
|
||||||
|
VXORV V4, V9, V4
|
||||||
|
VROTRW $20, V5, V5
|
||||||
|
VROTRW $20, V6, V6
|
||||||
|
VROTRW $20, V7, V7
|
||||||
|
VROTRW $20, V4, V4
|
||||||
|
|
||||||
|
// V0..V3 += V5..V7, V4
|
||||||
|
// V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 8)
|
||||||
|
VADDW V5, V0, V0
|
||||||
|
VADDW V6, V1, V1
|
||||||
|
VADDW V7, V2, V2
|
||||||
|
VADDW V4, V3, V3
|
||||||
|
VXORV V15, V0, V15
|
||||||
|
VXORV V12, V1, V12
|
||||||
|
VXORV V13, V2, V13
|
||||||
|
VXORV V14, V3, V14
|
||||||
|
VROTRW $24, V15, V15
|
||||||
|
VROTRW $24, V12, V12
|
||||||
|
VROTRW $24, V13, V13
|
||||||
|
VROTRW $24, V14, V14
|
||||||
|
|
||||||
|
// V10,V11,V8,V9 += V15,V12,V13,V14
|
||||||
|
// V5,V6,V7,V4 <<<= ((V5,V6,V7,V4 XOR V10,V11,V8,V9), 7)
|
||||||
|
VADDW V15, V10, V10
|
||||||
|
VADDW V12, V11, V11
|
||||||
|
VADDW V13, V8, V8
|
||||||
|
VADDW V14, V9, V9
|
||||||
|
VXORV V5, V10, V5
|
||||||
|
VXORV V6, V11, V6
|
||||||
|
VXORV V7, V8, V7
|
||||||
|
VXORV V4, V9, V4
|
||||||
|
VROTRW $25, V5, V5
|
||||||
|
VROTRW $25, V6, V6
|
||||||
|
VROTRW $25, V7, V7
|
||||||
|
VROTRW $25, V4, V4
|
||||||
|
|
||||||
|
SUBV $1, R15
|
||||||
|
BNE R15, R0, chacha
|
||||||
|
|
||||||
|
// load origin contants
|
||||||
|
VMOVQ (R10), V16.W4
|
||||||
|
VMOVQ 4(R10), V17.W4
|
||||||
|
VMOVQ 8(R10), V18.W4
|
||||||
|
VMOVQ 12(R10), V19.W4
|
||||||
|
|
||||||
|
// load origin keys
|
||||||
|
VMOVQ (R7), V20.W4
|
||||||
|
VMOVQ 4(R7), V21.W4
|
||||||
|
VMOVQ 8(R7), V22.W4
|
||||||
|
VMOVQ 12(R7), V23.W4
|
||||||
|
VMOVQ 16(R7), V24.W4
|
||||||
|
VMOVQ 20(R7), V25.W4
|
||||||
|
VMOVQ 24(R7), V26.W4
|
||||||
|
VMOVQ 28(R7), V27.W4
|
||||||
|
|
||||||
|
// add back the initial state to generate the key stream
|
||||||
|
VADDW V30, V12, V12 // update counter in advance to prevent V30 from being overwritten
|
||||||
|
VADDW V16, V0, V0
|
||||||
|
VADDW V17, V1, V1
|
||||||
|
VADDW V18, V2, V2
|
||||||
|
VADDW V19, V3, V3
|
||||||
|
|
||||||
|
// load origin counter + nonce
|
||||||
|
VMOVQ (R9), V28.W4
|
||||||
|
VMOVQ (R8), V29.W4
|
||||||
|
VMOVQ 4(R8), V30.W4
|
||||||
|
VMOVQ 8(R8), V31.W4
|
||||||
|
|
||||||
|
VADDW V20, V4, V4
|
||||||
|
VADDW V21, V5, V5
|
||||||
|
VADDW V22, V6, V6
|
||||||
|
VADDW V23, V7, V7
|
||||||
|
VADDW V24, V8, V8
|
||||||
|
VADDW V25, V9, V9
|
||||||
|
VADDW V26, V10, V10
|
||||||
|
VADDW V27, V11, V11
|
||||||
|
VADDW V28, V12, V12
|
||||||
|
VADDW V29, V13, V13
|
||||||
|
VADDW V30, V14, V14
|
||||||
|
VADDW V31, V15, V15
|
||||||
|
|
||||||
|
// shuffle
|
||||||
|
VILVLW V0, V1, V16
|
||||||
|
VILVHW V0, V1, V17
|
||||||
|
VILVLW V2, V3, V18
|
||||||
|
VILVHW V2, V3, V19
|
||||||
|
VILVLW V4, V5 ,V20
|
||||||
|
VILVHW V4, V5, V21
|
||||||
|
VILVLW V6, V7, V22
|
||||||
|
VILVHW V6, V7, V23
|
||||||
|
VILVLW V8, V9, V24
|
||||||
|
VILVHW V8, V9, V25
|
||||||
|
VILVLW V10, V11, V26
|
||||||
|
VILVHW V10, V11, V27
|
||||||
|
VILVLW V12, V13, V28
|
||||||
|
VILVHW V12, V13, V29
|
||||||
|
VILVLW V14, V15, V30
|
||||||
|
VILVHW V14, V15, V31
|
||||||
|
VILVLV V16, V18, V0
|
||||||
|
VILVHV V16, V18, V4
|
||||||
|
VILVLV V17, V19, V8
|
||||||
|
VILVHV V17, V19, V12
|
||||||
|
|
||||||
|
// load src data from R5
|
||||||
|
VMOVQ 0(R5), V16
|
||||||
|
VMOVQ 16(R5), V17
|
||||||
|
VMOVQ 32(R5), V18
|
||||||
|
VMOVQ 48(R5), V19
|
||||||
|
|
||||||
|
VILVLV V20, V22, V1
|
||||||
|
VILVHV V20, V22, V5
|
||||||
|
VILVLV V21, V23, V9
|
||||||
|
VILVHV V21, V23, V13
|
||||||
|
|
||||||
|
VMOVQ 64(R5), V20
|
||||||
|
VMOVQ 80(R5), V21
|
||||||
|
VMOVQ 96(R5), V22
|
||||||
|
VMOVQ 112(R5), V23
|
||||||
|
|
||||||
|
VILVLV V24, V26, V2
|
||||||
|
VILVHV V24, V26, V6
|
||||||
|
VILVLV V25, V27, V10
|
||||||
|
VILVHV V25, V27, V14
|
||||||
|
|
||||||
|
VMOVQ 128(R5), V24
|
||||||
|
VMOVQ 144(R5), V25
|
||||||
|
VMOVQ 160(R5), V26
|
||||||
|
VMOVQ 176(R5), V27
|
||||||
|
|
||||||
|
VILVLV V28, V30, V3
|
||||||
|
VILVHV V28, V30, V7
|
||||||
|
VILVLV V29, V31, V11
|
||||||
|
VILVHV V29, V31, V15
|
||||||
|
|
||||||
|
VMOVQ 192(R5), V28
|
||||||
|
VMOVQ 208(R5), V29
|
||||||
|
VMOVQ 224(R5), V30
|
||||||
|
VMOVQ 240(R5), V31
|
||||||
|
|
||||||
|
VXORV V0, V16, V16
|
||||||
|
VXORV V1, V17, V17
|
||||||
|
VXORV V2, V18, V18
|
||||||
|
VXORV V3, V19, V19
|
||||||
|
|
||||||
|
VMOVQ V16, 0(R4)
|
||||||
|
VMOVQ V17, 16(R4)
|
||||||
|
VMOVQ V18, 32(R4)
|
||||||
|
VMOVQ V19, 48(R4)
|
||||||
|
|
||||||
|
VXORV V4, V20, V20
|
||||||
|
VXORV V5, V21, V21
|
||||||
|
VXORV V6, V22, V22
|
||||||
|
VXORV V7, V23, V23
|
||||||
|
|
||||||
|
VMOVQ V20, 64(R4)
|
||||||
|
VMOVQ V21, 80(R4)
|
||||||
|
VMOVQ V22, 96(R4)
|
||||||
|
VMOVQ V23, 112(R4)
|
||||||
|
|
||||||
|
VXORV V8, V24, V24
|
||||||
|
VXORV V9, V25, V25
|
||||||
|
VXORV V10, V26, V26
|
||||||
|
VXORV V11, V27, V27
|
||||||
|
|
||||||
|
VMOVQ V24, 128(R4)
|
||||||
|
VMOVQ V25, 144(R4)
|
||||||
|
VMOVQ V26, 160(R4)
|
||||||
|
VMOVQ V27, 176(R4)
|
||||||
|
|
||||||
|
VXORV V12, V28, V28
|
||||||
|
VXORV V13, V29, V29
|
||||||
|
VXORV V14, V30, V30
|
||||||
|
VXORV V15, V31, V31
|
||||||
|
|
||||||
|
VMOVQ V28, 192(R4)
|
||||||
|
VMOVQ V29, 208(R4)
|
||||||
|
VMOVQ V30, 224(R4)
|
||||||
|
VMOVQ V31, 240(R4)
|
||||||
|
|
||||||
|
ADD $4, R12, R12
|
||||||
|
MOVW R12, (R9) // update counter
|
||||||
|
|
||||||
|
ADDV $256, R4, R4
|
||||||
|
ADDV $256, R5, R5
|
||||||
|
SUBV $256, R6, R6
|
||||||
|
BNE R6, R0, loop
|
||||||
|
|
||||||
|
RET
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
|
//go:build (!arm64 && !loong64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
|
||||||
|
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,9 @@ type chacha20poly1305 struct {
|
|||||||
|
|
||||||
// New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key.
|
// New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key.
|
||||||
func New(key []byte) (cipher.AEAD, error) {
|
func New(key []byte) (cipher.AEAD, error) {
|
||||||
|
if fips140Enforced() {
|
||||||
|
return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode")
|
||||||
|
}
|
||||||
if len(key) != KeySize {
|
if len(key) != KeySize {
|
||||||
return nil, errors.New("chacha20poly1305: bad key length")
|
return nil, errors.New("chacha20poly1305: bad key length")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
|
|||||||
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
|
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2
|
useAVX2 = cpu.X86.HasSSSE3 && cpu.X86.HasAVX2 && cpu.X86.HasBMI2
|
||||||
)
|
)
|
||||||
|
|
||||||
// setupState writes a ChaCha20 input matrix to state. See
|
// setupState writes a ChaCha20 input matrix to state. See
|
||||||
@@ -47,7 +47,7 @@ func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
if !cpu.X86.HasSSSE3 {
|
if !useAVX2 {
|
||||||
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,14 +56,17 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []
|
|||||||
|
|
||||||
ret, out := sliceForAppend(dst, len(plaintext)+16)
|
ret, out := sliceForAppend(dst, len(plaintext)+16)
|
||||||
if alias.InexactOverlap(out, plaintext) {
|
if alias.InexactOverlap(out, plaintext) {
|
||||||
panic("chacha20poly1305: invalid buffer overlap")
|
panic("chacha20poly1305: invalid buffer overlap of output and input")
|
||||||
|
}
|
||||||
|
if alias.AnyOverlap(out, additionalData) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
|
||||||
}
|
}
|
||||||
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
|
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
if !cpu.X86.HasSSSE3 {
|
if !useAVX2 {
|
||||||
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +76,10 @@ func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) (
|
|||||||
ciphertext = ciphertext[:len(ciphertext)-16]
|
ciphertext = ciphertext[:len(ciphertext)-16]
|
||||||
ret, out := sliceForAppend(dst, len(ciphertext))
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
if alias.InexactOverlap(out, ciphertext) {
|
if alias.InexactOverlap(out, ciphertext) {
|
||||||
panic("chacha20poly1305: invalid buffer overlap")
|
panic("chacha20poly1305: invalid buffer overlap of output and input")
|
||||||
|
}
|
||||||
|
if alias.AnyOverlap(out, additionalData) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
|
||||||
}
|
}
|
||||||
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
|
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
|
||||||
for i := range out {
|
for i := range out {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,10 @@ func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []b
|
|||||||
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
|
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
|
||||||
ciphertext, tag := out[:len(plaintext)], out[len(plaintext):]
|
ciphertext, tag := out[:len(plaintext)], out[len(plaintext):]
|
||||||
if alias.InexactOverlap(out, plaintext) {
|
if alias.InexactOverlap(out, plaintext) {
|
||||||
panic("chacha20poly1305: invalid buffer overlap")
|
panic("chacha20poly1305: invalid buffer overlap of output and input")
|
||||||
|
}
|
||||||
|
if alias.AnyOverlap(out, additionalData) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
|
||||||
}
|
}
|
||||||
|
|
||||||
var polyKey [32]byte
|
var polyKey [32]byte
|
||||||
@@ -67,7 +70,10 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
|
|||||||
|
|
||||||
ret, out := sliceForAppend(dst, len(ciphertext))
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
if alias.InexactOverlap(out, ciphertext) {
|
if alias.InexactOverlap(out, ciphertext) {
|
||||||
panic("chacha20poly1305: invalid buffer overlap")
|
panic("chacha20poly1305: invalid buffer overlap of output and input")
|
||||||
|
}
|
||||||
|
if alias.AnyOverlap(out, additionalData) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
|
||||||
}
|
}
|
||||||
if !p.Verify(tag) {
|
if !p.Verify(tag) {
|
||||||
for i := range out {
|
for i := range out {
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ func TestRandom(t *testing.T) {
|
|||||||
t.Run("X", func(t *testing.T) { f(t, NonceSizeX) })
|
t.Run("X", func(t *testing.T) { f(t, NonceSizeX) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte, nonceSize int) {
|
func benchmarkChaCha20Poly1305Seal(b *testing.B, buf []byte, nonceSize int) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.SetBytes(int64(len(buf)))
|
b.SetBytes(int64(len(buf)))
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte, nonceSize int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte, nonceSize int) {
|
func benchmarkChaCha20Poly1305Open(b *testing.B, buf []byte, nonceSize int) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.SetBytes(int64(len(buf)))
|
b.SetBytes(int64(len(buf)))
|
||||||
|
|
||||||
@@ -204,17 +204,17 @@ func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte, nonceSize int) {
|
|||||||
func BenchmarkChacha20Poly1305(b *testing.B) {
|
func BenchmarkChacha20Poly1305(b *testing.B) {
|
||||||
for _, length := range []int{64, 1350, 8 * 1024} {
|
for _, length := range []int{64, 1350, 8 * 1024} {
|
||||||
b.Run("Open-"+strconv.Itoa(length), func(b *testing.B) {
|
b.Run("Open-"+strconv.Itoa(length), func(b *testing.B) {
|
||||||
benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSize)
|
benchmarkChaCha20Poly1305Open(b, make([]byte, length), NonceSize)
|
||||||
})
|
})
|
||||||
b.Run("Seal-"+strconv.Itoa(length), func(b *testing.B) {
|
b.Run("Seal-"+strconv.Itoa(length), func(b *testing.B) {
|
||||||
benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSize)
|
benchmarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSize)
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("Open-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
|
b.Run("Open-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
|
||||||
benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSizeX)
|
benchmarkChaCha20Poly1305Open(b, make([]byte, length), NonceSizeX)
|
||||||
})
|
})
|
||||||
b.Run("Seal-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
|
b.Run("Seal-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
|
||||||
benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSizeX)
|
benchmarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSizeX)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.25
|
//go:build !go1.26
|
||||||
|
|
||||||
package blake2b
|
package chacha20poly1305
|
||||||
|
|
||||||
import "hash"
|
func fips140Enforced() bool { return false }
|
||||||
|
|
||||||
var _ hash.XOF = (*xof)(nil)
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.26
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
import "crypto/fips140"
|
||||||
|
|
||||||
|
func fips140Enforced() bool { return fips140.Enforced() }
|
||||||
@@ -22,6 +22,9 @@ type xchacha20poly1305 struct {
|
|||||||
// preferred when nonce uniqueness cannot be trivially ensured, or whenever
|
// preferred when nonce uniqueness cannot be trivially ensured, or whenever
|
||||||
// nonces are randomly generated.
|
// nonces are randomly generated.
|
||||||
func NewX(key []byte) (cipher.AEAD, error) {
|
func NewX(key []byte) (cipher.AEAD, error) {
|
||||||
|
if fips140Enforced() {
|
||||||
|
return nil, errors.New("chacha20poly1305: use of ChaCha20Poly1305 is not allowed in FIPS 140-only mode")
|
||||||
|
}
|
||||||
if len(key) != KeySize {
|
if len(key) != KeySize {
|
||||||
return nil, errors.New("chacha20poly1305: bad key length")
|
return nil, errors.New("chacha20poly1305: bad key length")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package curve25519 provides an implementation of the X25519 function, which
|
// Package curve25519 provides an implementation of the X25519 function, which
|
||||||
// performs scalar multiplication on the elliptic curve known as Curve25519.
|
// performs scalar multiplication on the elliptic curve known as Curve25519
|
||||||
// See RFC 7748.
|
// according to [RFC 7748].
|
||||||
//
|
//
|
||||||
// This package is a wrapper for the X25519 implementation
|
// The curve25519 package is a wrapper for the X25519 implementation in the
|
||||||
// in the crypto/ecdh package.
|
// crypto/ecdh package. It is [frozen] and is not accepting new features.
|
||||||
|
//
|
||||||
|
// [RFC 7748]: https://datatracker.ietf.org/doc/html/rfc7748
|
||||||
|
// [frozen]: https://go.dev/wiki/Frozen
|
||||||
package curve25519
|
package curve25519
|
||||||
|
|
||||||
import "crypto/ecdh"
|
import "crypto/ecdh"
|
||||||
@@ -36,7 +39,7 @@ func ScalarBaseMult(dst, scalar *[32]byte) {
|
|||||||
curve := ecdh.X25519()
|
curve := ecdh.X25519()
|
||||||
priv, err := curve.NewPrivateKey(scalar[:])
|
priv, err := curve.NewPrivateKey(scalar[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("curve25519: internal error: scalarBaseMult was not 32 bytes")
|
panic("curve25519: " + err.Error())
|
||||||
}
|
}
|
||||||
copy(dst[:], priv.PublicKey().Bytes())
|
copy(dst[:], priv.PublicKey().Bytes())
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -2,16 +2,19 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
// Package ed25519 implements the Ed25519 signature algorithm.
|
||||||
// https://ed25519.cr.yp.to/.
|
|
||||||
//
|
//
|
||||||
// These functions are also compatible with the “Ed25519” function defined in
|
// These functions are also compatible with the “Ed25519” function defined in
|
||||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
// [RFC 8032]. However, unlike RFC 8032's formulation, this package's private key
|
||||||
// representation includes a public key suffix to make multiple signing
|
// representation includes a public key suffix to make multiple signing
|
||||||
// operations with the same key more efficient. This package refers to the RFC
|
// operations with the same key more efficient. This package refers to the RFC
|
||||||
// 8032 private key as the “seed”.
|
// 8032 private key as the “seed”.
|
||||||
//
|
//
|
||||||
// This package is a wrapper around the standard library crypto/ed25519 package.
|
// The ed25519 package is a wrapper for the Ed25519 implementation in the
|
||||||
|
// crypto/ed25519 package. It is [frozen] and is not accepting new features.
|
||||||
|
//
|
||||||
|
// [RFC 8032]: https://datatracker.ietf.org/doc/html/rfc8032
|
||||||
|
// [frozen]: https://go.dev/wiki/Frozen
|
||||||
package ed25519
|
package ed25519
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
module golang.org/x/crypto
|
module golang.org/x/crypto
|
||||||
|
|
||||||
go 1.23.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/net v0.41.0 // tagx:ignore
|
golang.org/x/net v0.58.0 // tagx:ignore
|
||||||
golang.org/x/sys v0.34.0
|
golang.org/x/sys v0.48.0
|
||||||
golang.org/x/term v0.33.0
|
golang.org/x/term v0.46.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/text v0.27.0 // indirect
|
require golang.org/x/text v0.42.0 // indirect
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||||
|
|||||||
+13
-8
@@ -11,6 +11,7 @@
|
|||||||
package hkdf
|
package hkdf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/hkdf"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"errors"
|
"errors"
|
||||||
"hash"
|
"hash"
|
||||||
@@ -24,15 +25,19 @@ import (
|
|||||||
// Expand invocations and different context values. Most common scenarios,
|
// Expand invocations and different context values. Most common scenarios,
|
||||||
// including the generation of multiple keys, should use New instead.
|
// including the generation of multiple keys, should use New instead.
|
||||||
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
||||||
if salt == nil {
|
// Use the stdlib Extract, which disables FIPS 140 enforcement of the HMAC
|
||||||
salt = make([]byte, hash().Size())
|
// key (which in HKDF is the salt). The only possible error is FIPS 140
|
||||||
|
// enforcement of the hash, which had to panic under this API anyway. We
|
||||||
|
// don't use the stdlib Expand, because it switched to returning a []byte
|
||||||
|
// instead of an io.Reader, and Expand uses the HMAC key as a key.
|
||||||
|
out, err := hkdf.Extract(hash, secret, salt)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
extractor := hmac.New(hash, salt)
|
return out
|
||||||
extractor.Write(secret)
|
|
||||||
return extractor.Sum(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type hkdf struct {
|
type hkdfReader struct {
|
||||||
expander hash.Hash
|
expander hash.Hash
|
||||||
size int
|
size int
|
||||||
|
|
||||||
@@ -43,7 +48,7 @@ type hkdf struct {
|
|||||||
buf []byte
|
buf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *hkdf) Read(p []byte) (int, error) {
|
func (f *hkdfReader) Read(p []byte) (int, error) {
|
||||||
// Check whether enough data can be generated
|
// Check whether enough data can be generated
|
||||||
need := len(p)
|
need := len(p)
|
||||||
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
||||||
@@ -84,7 +89,7 @@ func (f *hkdf) Read(p []byte) (int, error) {
|
|||||||
// 3.3. Most common scenarios will want to use New instead.
|
// 3.3. Most common scenarios will want to use New instead.
|
||||||
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
||||||
expander := hmac.New(hash, pseudorandomKey)
|
expander := hmac.New(hash, pseudorandomKey)
|
||||||
return &hkdf{expander, expander.Size(), info, 1, nil, nil}
|
return &hkdfReader{expander, expander.Size(), info, 1, nil, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a Reader, from which keys can be read, using the given hash,
|
// New returns a Reader, from which keys can be read, using the given hash,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !s390x) || !gc || purego
|
//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !riscv64 && !s390x) || !gc || purego
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le)
|
//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le || riscv64)
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gc && !purego
|
||||||
|
|
||||||
|
#define LOAD64U(base, offset, t0, t1, t2, t3, dst) \
|
||||||
|
MOVBU (offset+0*1)(base), t0; \
|
||||||
|
MOVBU (offset+1*1)(base), t1; \
|
||||||
|
MOVBU (offset+2*1)(base), t2; \
|
||||||
|
MOVBU (offset+3*1)(base), t3; \
|
||||||
|
SLL $8, t1; \
|
||||||
|
SLL $16, t2; \
|
||||||
|
SLL $24, t3; \
|
||||||
|
OR t1, t0; \
|
||||||
|
OR t3, t2; \
|
||||||
|
OR t2, t0, dst; \
|
||||||
|
MOVBU (offset+4*1)(base), t0; \
|
||||||
|
MOVBU (offset+5*1)(base), t1; \
|
||||||
|
MOVBU (offset+6*1)(base), t2; \
|
||||||
|
MOVBU (offset+7*1)(base), t3; \
|
||||||
|
SLL $32, t0; \
|
||||||
|
SLL $40, t1; \
|
||||||
|
SLL $48, t2; \
|
||||||
|
SLL $56, t3; \
|
||||||
|
OR t1, t0; \
|
||||||
|
OR t3, t2; \
|
||||||
|
OR t2, t0; \
|
||||||
|
OR t0, dst
|
||||||
|
|
||||||
|
// func update(state *macState, msg []byte)
|
||||||
|
TEXT ·update(SB), $0-32
|
||||||
|
MOV state+0(FP), X5
|
||||||
|
MOV msg_base+8(FP), X6
|
||||||
|
MOV msg_len+16(FP), X7
|
||||||
|
|
||||||
|
MOV $16, X8
|
||||||
|
|
||||||
|
AND $7, X6, X28
|
||||||
|
|
||||||
|
MOV (0*8)(X5), X9 // h0
|
||||||
|
MOV (1*8)(X5), X10 // h1
|
||||||
|
MOV (2*8)(X5), X11 // h2
|
||||||
|
MOV (3*8)(X5), X12 // r0
|
||||||
|
MOV (4*8)(X5), X13 // r1
|
||||||
|
|
||||||
|
BLT X7, X8, tail
|
||||||
|
|
||||||
|
loop:
|
||||||
|
BEQZ X28, aligned_load
|
||||||
|
|
||||||
|
LOAD64U(X6, 0*8, X16, X18, X19, X20, X15) // msg[0:8]
|
||||||
|
LOAD64U(X6, 1*8, X16, X18, X19, X20, X17) // msg[8:16]
|
||||||
|
JMP block
|
||||||
|
|
||||||
|
aligned_load:
|
||||||
|
MOV (0*8)(X6), X15 // msg[0:8]
|
||||||
|
MOV (1*8)(X6), X17 // msg[8:16]
|
||||||
|
|
||||||
|
block:
|
||||||
|
ADD X15, X9 // h0 (x1 + y1 = z1', if z1' < x1 then z1' overflow)
|
||||||
|
SLTU X15, X9, X19 // h0.carry
|
||||||
|
ADD X17, X10, X22
|
||||||
|
SLTU X17, X22, X23
|
||||||
|
ADD X22, X19, X10 // h1
|
||||||
|
SLTU X22, X10, X19
|
||||||
|
OR X23, X19 // h1.carry
|
||||||
|
ADD $1, X19
|
||||||
|
ADD X19, X11 // h2
|
||||||
|
|
||||||
|
ADD $16, X6 // msg = msg[16:]
|
||||||
|
|
||||||
|
multiply:
|
||||||
|
MULHU X9, X12, X16 // h0r0.hi
|
||||||
|
MUL X9, X12, X15 // h0r0.lo
|
||||||
|
MULHU X10, X12, X17 // h1r0.hi
|
||||||
|
MUL X10, X12, X14 // h1r0.lo
|
||||||
|
ADD X14, X16
|
||||||
|
SLTU X14, X16, X19
|
||||||
|
ADD X19, X17
|
||||||
|
MUL X11, X12, X20
|
||||||
|
ADD X17, X20
|
||||||
|
MULHU X9, X13, X17 // h0r1.hi
|
||||||
|
MUL X9, X13, X14 // h0r1.lo
|
||||||
|
ADD X14, X16
|
||||||
|
SLTU X14, X16, X19
|
||||||
|
ADD X19, X17
|
||||||
|
MOV X17, X9
|
||||||
|
MUL X11, X13, X21 // h2r1
|
||||||
|
MULHU X10, X13, X17 // h1r1.hi
|
||||||
|
MUL X10, X13, X14 // h1r1.lo
|
||||||
|
ADD X14, X20
|
||||||
|
ADD X17, X21, X22
|
||||||
|
SLTU X14, X20, X19
|
||||||
|
ADD X22, X19, X21
|
||||||
|
ADD X9, X20
|
||||||
|
SLTU X9, X20, X19
|
||||||
|
ADD X19, X21
|
||||||
|
AND $3, X20, X11
|
||||||
|
AND $-4, X20, X18
|
||||||
|
ADD X18, X15, X9
|
||||||
|
ADD X21, X16, X22
|
||||||
|
SLTU X18, X9, X19
|
||||||
|
SLTU X21, X22, X23
|
||||||
|
ADD X22, X19, X10
|
||||||
|
SLTU X22, X10, X19
|
||||||
|
OR X19, X23, X19
|
||||||
|
ADD X19, X11
|
||||||
|
SLL $62, X21, X22
|
||||||
|
SRL $2, X20, X23
|
||||||
|
SRL $2, X21, X21
|
||||||
|
OR X22, X23, X20
|
||||||
|
ADD X20, X9, X9
|
||||||
|
ADD X21, X10, X22
|
||||||
|
SLTU X20, X9, X19
|
||||||
|
SLTU X21, X22, X23
|
||||||
|
ADD X22, X19, X10
|
||||||
|
SLTU X22, X10, X19
|
||||||
|
OR X19, X23, X19
|
||||||
|
ADD X19, X11, X11
|
||||||
|
|
||||||
|
SUB $16, X7, X7
|
||||||
|
BGE X7, X8, loop
|
||||||
|
|
||||||
|
tail:
|
||||||
|
BEQ X7, X0, done
|
||||||
|
MOV $1, X15
|
||||||
|
MOV $0, X16
|
||||||
|
ADD X7, X6, X6
|
||||||
|
|
||||||
|
flush_buffer:
|
||||||
|
MOVBU -1(X6), X20
|
||||||
|
SRL $56, X15, X19
|
||||||
|
SLL $8, X16, X23
|
||||||
|
SLL $8, X15, X15
|
||||||
|
OR X19, X23, X16
|
||||||
|
XOR X20, X15
|
||||||
|
SUB $1, X7, X7
|
||||||
|
SUB $1, X6, X6
|
||||||
|
BNE X7, X0, flush_buffer
|
||||||
|
|
||||||
|
ADD X15, X9
|
||||||
|
SLTU X15, X9, X19
|
||||||
|
ADD X16, X10, X22
|
||||||
|
SLTU X16, X22, X23
|
||||||
|
ADD X22, X19, X10
|
||||||
|
SLTU X22, X10, X19
|
||||||
|
OR X23, X19
|
||||||
|
ADD X19, X11
|
||||||
|
|
||||||
|
MOV $16, X7
|
||||||
|
JMP multiply
|
||||||
|
|
||||||
|
done:
|
||||||
|
MOV X9, (0*8)(X5) // h0
|
||||||
|
MOV X10, (1*8)(X5)
|
||||||
|
MOV X11, (2*8)(X5)
|
||||||
|
RET
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
This package runs a set of the Wycheproof tests provided by
|
|
||||||
https://github.com/google/wycheproof.
|
|
||||||
|
|
||||||
The JSON test files live in
|
|
||||||
https://github.com/google/wycheproof/tree/master/testvectors
|
|
||||||
and are being fetched and cached at a pinned version every time
|
|
||||||
these tests are run. To change the version of the wycheproof
|
|
||||||
repository that is being used for testing, update wycheproofModVer.
|
|
||||||
|
|
||||||
The structs for these tests are generated from the
|
|
||||||
schemas provided in https://github.com/google/wycheproof/tree/master/schemas
|
|
||||||
using https://github.com/a-h/generate.
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
// Copyright 2020 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 wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAEAD(t *testing.T) {
|
|
||||||
// AeadTestVector
|
|
||||||
type AeadTestVector struct {
|
|
||||||
|
|
||||||
// additional authenticated data
|
|
||||||
Aad string `json:"aad,omitempty"`
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// the ciphertext (without iv and tag)
|
|
||||||
Ct string `json:"ct,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// the nonce
|
|
||||||
Iv string `json:"iv,omitempty"`
|
|
||||||
|
|
||||||
// the key
|
|
||||||
Key string `json:"key,omitempty"`
|
|
||||||
|
|
||||||
// the plaintext
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// the authentication tag
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// AeadTestGroup
|
|
||||||
type AeadTestGroup struct {
|
|
||||||
|
|
||||||
// the IV size in bits
|
|
||||||
IvSize int `json:"ivSize,omitempty"`
|
|
||||||
|
|
||||||
// the keySize in bits
|
|
||||||
KeySize int `json:"keySize,omitempty"`
|
|
||||||
|
|
||||||
// the expected size of the tag in bits
|
|
||||||
TagSize int `json:"tagSize,omitempty"`
|
|
||||||
Tests []*AeadTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*AeadTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
testSealOpen := func(t *testing.T, aead cipher.AEAD, tv *AeadTestVector, recoverBadNonce func()) {
|
|
||||||
defer recoverBadNonce()
|
|
||||||
|
|
||||||
iv, tag, ct, msg, aad := decodeHex(tv.Iv), decodeHex(tv.Tag), decodeHex(tv.Ct), decodeHex(tv.Msg), decodeHex(tv.Aad)
|
|
||||||
|
|
||||||
genCT := aead.Seal(nil, iv, msg, aad)
|
|
||||||
genMsg, err := aead.Open(nil, iv, genCT, aad)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to decrypt generated ciphertext: %s", err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(genMsg, msg) {
|
|
||||||
t.Errorf("unexpected roundtripped plaintext: got %x, want %x", genMsg, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctWithTag := append(ct, tag...)
|
|
||||||
msg2, err := aead.Open(nil, iv, ctWithTag, aad)
|
|
||||||
wantPass := shouldPass(tv.Result, tv.Flags, nil)
|
|
||||||
if !wantPass && err == nil {
|
|
||||||
t.Error("decryption succeeded when it should've failed")
|
|
||||||
} else if wantPass {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("decryption failed: %s", err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(genCT, ctWithTag) {
|
|
||||||
t.Errorf("generated ciphertext doesn't match expected: got %x, want %x", genCT, ctWithTag)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(msg, msg2) {
|
|
||||||
t.Errorf("decrypted ciphertext doesn't match expected: got %x, want %x", msg2, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vectors := map[string]func(*testing.T, []byte) cipher.AEAD{
|
|
||||||
"aes_gcm_test.json": func(t *testing.T, key []byte) cipher.AEAD {
|
|
||||||
aesCipher, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to construct cipher: %s", err)
|
|
||||||
}
|
|
||||||
aead, err := cipher.NewGCM(aesCipher)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to construct cipher: %s", err)
|
|
||||||
}
|
|
||||||
return aead
|
|
||||||
},
|
|
||||||
"chacha20_poly1305_test.json": func(t *testing.T, key []byte) cipher.AEAD {
|
|
||||||
aead, err := chacha20poly1305.New(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to construct cipher: %s", err)
|
|
||||||
}
|
|
||||||
return aead
|
|
||||||
},
|
|
||||||
"xchacha20_poly1305_test.json": func(t *testing.T, key []byte) cipher.AEAD {
|
|
||||||
aead, err := chacha20poly1305.NewX(key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to construct cipher: %s", err)
|
|
||||||
}
|
|
||||||
return aead
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for file, cipherInit := range vectors {
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, file, &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
for _, tv := range tg.Tests {
|
|
||||||
testName := fmt.Sprintf("%s #%d", file, tv.TcId)
|
|
||||||
if tv.Comment != "" {
|
|
||||||
testName += fmt.Sprintf(" %s", tv.Comment)
|
|
||||||
}
|
|
||||||
t.Run(testName, func(t *testing.T) {
|
|
||||||
aead := cipherInit(t, decodeHex(tv.Key))
|
|
||||||
testSealOpen(t, aead, tv, func() {
|
|
||||||
// A bad nonce causes a panic in AEAD.Seal and AEAD.Open,
|
|
||||||
// so should be recovered. Fail the test if it broke for
|
|
||||||
// some other reason.
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if tg.IvSize/8 == aead.NonceSize() {
|
|
||||||
t.Error("unexpected panic")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAesCbc(t *testing.T) {
|
|
||||||
// IndCpaTestVector
|
|
||||||
type IndCpaTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// the raw ciphertext (without IV)
|
|
||||||
Ct string `json:"ct,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// the initialization vector
|
|
||||||
Iv string `json:"iv,omitempty"`
|
|
||||||
|
|
||||||
// the key
|
|
||||||
Key string `json:"key,omitempty"`
|
|
||||||
|
|
||||||
// the plaintext
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndCpaTestGroup
|
|
||||||
type IndCpaTestGroup struct {
|
|
||||||
|
|
||||||
// the IV size in bits
|
|
||||||
IvSize int `json:"ivSize,omitempty"`
|
|
||||||
|
|
||||||
// the keySize in bits
|
|
||||||
KeySize int `json:"keySize,omitempty"`
|
|
||||||
|
|
||||||
// the expected size of the tag in bits
|
|
||||||
TagSize int `json:"tagSize,omitempty"`
|
|
||||||
Tests []*IndCpaTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*IndCpaTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, "aes_cbc_pkcs5_test.json", &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
tests:
|
|
||||||
for _, tv := range tg.Tests {
|
|
||||||
block, err := aes.NewCipher(decodeHex(tv.Key))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("#%d: %v", tv.TcId, err)
|
|
||||||
}
|
|
||||||
mode := cipher.NewCBCDecrypter(block, decodeHex(tv.Iv))
|
|
||||||
ct := decodeHex(tv.Ct)
|
|
||||||
if len(ct)%aes.BlockSize != 0 {
|
|
||||||
panic(fmt.Sprintf("#%d: ciphertext is not a multiple of the block size", tv.TcId))
|
|
||||||
}
|
|
||||||
mode.CryptBlocks(ct, ct) // decrypt the block in place
|
|
||||||
|
|
||||||
// Skip the tests that are broken due to bad padding. Panic if there are any
|
|
||||||
// tests left that are invalid for some other reason in the future, to
|
|
||||||
// evaluate what to do with those tests.
|
|
||||||
for _, flag := range tv.Flags {
|
|
||||||
if flag == "BadPadding" {
|
|
||||||
continue tests
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !shouldPass(tv.Result, tv.Flags, nil) {
|
|
||||||
panic(fmt.Sprintf("#%d: found an invalid test that is broken for some reason other than bad padding", tv.TcId))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the PKCS#5 padding from the given ciphertext to validate it
|
|
||||||
padding := ct[len(ct)-1]
|
|
||||||
paddingNum := int(padding)
|
|
||||||
for i := paddingNum; i > 0; i-- {
|
|
||||||
if ct[len(ct)-i] != padding { // panic if the padding is unexpectedly bad
|
|
||||||
panic(fmt.Sprintf("#%d: bad padding at index=%d of %v", tv.TcId, i, ct))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ct = ct[:len(ct)-paddingNum]
|
|
||||||
|
|
||||||
if got, want := hex.EncodeToString(ct), tv.Msg; got != want {
|
|
||||||
t.Errorf("#%d, type: %s, comment: %q, decoded ciphertext not equal: %s, want %s", tv.TcId, tv.Result, tv.Comment, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright 2022 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 boringcrypto
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
const boringcryptoEnabled = true
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/dsa"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
wdsa "golang.org/x/crypto/internal/wycheproof/internal/dsa"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDsa(t *testing.T) {
|
|
||||||
// AsnSignatureTestVector
|
|
||||||
type AsnSignatureTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// The message to sign
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// An ASN encoded signature for msg
|
|
||||||
Sig string `json:"sig,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DsaPublicKey
|
|
||||||
type DsaPublicKey struct {
|
|
||||||
|
|
||||||
// the generator of the multiplicative subgroup
|
|
||||||
G string `json:"g,omitempty"`
|
|
||||||
|
|
||||||
// the key size in bits
|
|
||||||
KeySize int `json:"keySize,omitempty"`
|
|
||||||
|
|
||||||
// the modulus p
|
|
||||||
P string `json:"p,omitempty"`
|
|
||||||
|
|
||||||
// the order of the generator g
|
|
||||||
Q string `json:"q,omitempty"`
|
|
||||||
|
|
||||||
// the key type
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
|
|
||||||
// the public key value
|
|
||||||
Y string `json:"y,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DsaTestGroup
|
|
||||||
type DsaTestGroup struct {
|
|
||||||
|
|
||||||
// unenocded DSA public key
|
|
||||||
Key *DsaPublicKey `json:"key,omitempty"`
|
|
||||||
|
|
||||||
// DER encoded public key
|
|
||||||
KeyDer string `json:"keyDer,omitempty"`
|
|
||||||
|
|
||||||
// Pem encoded public key
|
|
||||||
KeyPem string `json:"keyPem,omitempty"`
|
|
||||||
|
|
||||||
// the hash function used for DSA
|
|
||||||
Sha string `json:"sha,omitempty"`
|
|
||||||
Tests []*AsnSignatureTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*DsaTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsShouldPass := map[string]bool{
|
|
||||||
// An encoded ASN.1 integer missing a leading zero is invalid, but accepted by some implementations.
|
|
||||||
"NoLeadingZero": false,
|
|
||||||
}
|
|
||||||
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, "dsa_test.json", &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
pub := decodePublicKey(tg.KeyDer).(*dsa.PublicKey)
|
|
||||||
h := parseHash(tg.Sha).New()
|
|
||||||
for _, sig := range tg.Tests {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(decodeHex(sig.Msg))
|
|
||||||
hashed := h.Sum(nil)
|
|
||||||
hashed = hashed[:pub.Q.BitLen()/8] // Truncate to the byte-length of the subgroup (Q)
|
|
||||||
got := wdsa.VerifyASN1(pub, hashed, decodeHex(sig.Sig))
|
|
||||||
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
|
|
||||||
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcId, sig.Result, sig.Comment, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
// Copyright 2022 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 wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/ecdh"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestECDHStdLib(t *testing.T) {
|
|
||||||
type ECDHTestVector struct {
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
// the private key
|
|
||||||
Private string `json:"private,omitempty"`
|
|
||||||
// Encoded public key
|
|
||||||
Public string `json:"public,omitempty"`
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
// The shared secret key
|
|
||||||
Shared string `json:"shared,omitempty"`
|
|
||||||
// Identifier of the test case
|
|
||||||
TcID int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ECDHTestGroup struct {
|
|
||||||
Curve string `json:"curve,omitempty"`
|
|
||||||
Tests []*ECDHTestVector `json:"tests,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Root struct {
|
|
||||||
TestGroups []*ECDHTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsShouldPass := map[string]bool{
|
|
||||||
// We don't support compressed points.
|
|
||||||
"CompressedPoint": false,
|
|
||||||
// We don't support decoding custom curves.
|
|
||||||
"UnnamedCurve": false,
|
|
||||||
// WrongOrder and UnusedParam are only found with UnnamedCurve.
|
|
||||||
"WrongOrder": false,
|
|
||||||
"UnusedParam": false,
|
|
||||||
|
|
||||||
// X25519 specific flags
|
|
||||||
"Twist": true,
|
|
||||||
"SmallPublicKey": false,
|
|
||||||
"LowOrderPublic": false,
|
|
||||||
"ZeroSharedSecret": false,
|
|
||||||
"NonCanonicalPublic": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// curveToCurve is a map of all elliptic curves supported
|
|
||||||
// by crypto/elliptic, which can subsequently be parsed and tested.
|
|
||||||
curveToCurve := map[string]ecdh.Curve{
|
|
||||||
"secp256r1": ecdh.P256(),
|
|
||||||
"secp384r1": ecdh.P384(),
|
|
||||||
"secp521r1": ecdh.P521(),
|
|
||||||
"curve25519": ecdh.X25519(),
|
|
||||||
}
|
|
||||||
|
|
||||||
curveToKeySize := map[string]int{
|
|
||||||
"secp256r1": 32,
|
|
||||||
"secp384r1": 48,
|
|
||||||
"secp521r1": 66,
|
|
||||||
"curve25519": 32,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range []string{
|
|
||||||
"ecdh_secp256r1_ecpoint_test.json",
|
|
||||||
"ecdh_secp384r1_ecpoint_test.json",
|
|
||||||
"ecdh_secp521r1_ecpoint_test.json",
|
|
||||||
"x25519_test.json",
|
|
||||||
} {
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, f, &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
if _, ok := curveToCurve[tg.Curve]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, tt := range tg.Tests {
|
|
||||||
tg, tt := tg, tt
|
|
||||||
t.Run(fmt.Sprintf("%s/%d", tg.Curve, tt.TcID), func(t *testing.T) {
|
|
||||||
t.Logf("Type: %v", tt.Result)
|
|
||||||
t.Logf("Flags: %q", tt.Flags)
|
|
||||||
t.Log(tt.Comment)
|
|
||||||
|
|
||||||
shouldPass := shouldPass(tt.Result, tt.Flags, flagsShouldPass)
|
|
||||||
|
|
||||||
curve := curveToCurve[tg.Curve]
|
|
||||||
p := decodeHex(tt.Public)
|
|
||||||
pub, err := curve.NewPublicKey(p)
|
|
||||||
if err != nil {
|
|
||||||
if shouldPass {
|
|
||||||
t.Errorf("NewPublicKey: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
privBytes := decodeHex(tt.Private)
|
|
||||||
if len(privBytes) != curveToKeySize[tg.Curve] {
|
|
||||||
t.Skipf("non-standard key size %d", len(privBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, err := curve.NewPrivateKey(privBytes)
|
|
||||||
if err != nil {
|
|
||||||
if shouldPass {
|
|
||||||
t.Errorf("NewPrivateKey: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
shared := decodeHex(tt.Shared)
|
|
||||||
x, err := priv.ECDH(pub)
|
|
||||||
if err != nil {
|
|
||||||
if tg.Curve == "curve25519" && !shouldPass {
|
|
||||||
// ECDH is expected to only return an error when using X25519,
|
|
||||||
// in all other cases an error is unexpected.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Fatalf("ECDH: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.Equal(shared, x) != shouldPass {
|
|
||||||
if shouldPass {
|
|
||||||
t.Errorf("ECDH = %x, want %x", shared, x)
|
|
||||||
} else {
|
|
||||||
t.Errorf("ECDH = %x, want anything else", shared)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/asn1"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
casn1 "golang.org/x/crypto/cryptobyte/asn1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestECDH(t *testing.T) {
|
|
||||||
type ECDHTestVector struct {
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
// the private key
|
|
||||||
Private string `json:"private,omitempty"`
|
|
||||||
// Encoded public key
|
|
||||||
Public string `json:"public,omitempty"`
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
// The shared secret key
|
|
||||||
Shared string `json:"shared,omitempty"`
|
|
||||||
// Identifier of the test case
|
|
||||||
TcID int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ECDHTestGroup struct {
|
|
||||||
Curve string `json:"curve,omitempty"`
|
|
||||||
Tests []*ECDHTestVector `json:"tests,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Root struct {
|
|
||||||
TestGroups []*ECDHTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsShouldPass := map[string]bool{
|
|
||||||
// ParsePKIXPublicKey doesn't support compressed points, but we test
|
|
||||||
// them against UnmarshalCompressed anyway.
|
|
||||||
"CompressedPoint": true,
|
|
||||||
// We don't support decoding custom curves.
|
|
||||||
"UnnamedCurve": false,
|
|
||||||
// WrongOrder and UnusedParam are only found with UnnamedCurve.
|
|
||||||
"WrongOrder": false,
|
|
||||||
"UnusedParam": false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// supportedCurves is a map of all elliptic curves supported
|
|
||||||
// by crypto/elliptic, which can subsequently be parsed and tested.
|
|
||||||
supportedCurves := map[string]bool{
|
|
||||||
"secp224r1": true,
|
|
||||||
"secp256r1": true,
|
|
||||||
"secp384r1": true,
|
|
||||||
"secp521r1": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, "ecdh_test.json", &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
if !supportedCurves[tg.Curve] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, tt := range tg.Tests {
|
|
||||||
tg, tt := tg, tt
|
|
||||||
t.Run(fmt.Sprintf("%s/%d", tg.Curve, tt.TcID), func(t *testing.T) {
|
|
||||||
t.Logf("Type: %v", tt.Result)
|
|
||||||
t.Logf("Flags: %q", tt.Flags)
|
|
||||||
t.Log(tt.Comment)
|
|
||||||
|
|
||||||
shouldPass := shouldPass(tt.Result, tt.Flags, flagsShouldPass)
|
|
||||||
|
|
||||||
p := decodeHex(tt.Public)
|
|
||||||
pp, err := x509.ParsePKIXPublicKey(p)
|
|
||||||
if err != nil {
|
|
||||||
pp, err = decodeCompressedPKIX(p)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if shouldPass {
|
|
||||||
t.Errorf("unexpected parsing error: %s", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pub := pp.(*ecdsa.PublicKey)
|
|
||||||
|
|
||||||
priv := decodeHex(tt.Private)
|
|
||||||
shared := decodeHex(tt.Shared)
|
|
||||||
|
|
||||||
x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, priv)
|
|
||||||
xBytes := make([]byte, (pub.Curve.Params().BitSize+7)/8)
|
|
||||||
got := bytes.Equal(shared, x.FillBytes(xBytes))
|
|
||||||
|
|
||||||
if want := shouldPass; got != want {
|
|
||||||
t.Errorf("wanted success %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeCompressedPKIX(der []byte) (interface{}, error) {
|
|
||||||
s := cryptobyte.String(der)
|
|
||||||
var s1, s2 cryptobyte.String
|
|
||||||
var algoOID, namedCurveOID asn1.ObjectIdentifier
|
|
||||||
var pointDER []byte
|
|
||||||
if !s.ReadASN1(&s1, casn1.SEQUENCE) || !s.Empty() ||
|
|
||||||
!s1.ReadASN1(&s2, casn1.SEQUENCE) ||
|
|
||||||
!s2.ReadASN1ObjectIdentifier(&algoOID) ||
|
|
||||||
!s2.ReadASN1ObjectIdentifier(&namedCurveOID) || !s2.Empty() ||
|
|
||||||
!s1.ReadASN1BitStringAsBytes(&pointDER) || !s1.Empty() {
|
|
||||||
return nil, errors.New("failed to parse PKIX structure")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !algoOID.Equal(oidPublicKeyECDSA) {
|
|
||||||
return nil, errors.New("wrong algorithm OID")
|
|
||||||
}
|
|
||||||
namedCurve := namedCurveFromOID(namedCurveOID)
|
|
||||||
if namedCurve == nil {
|
|
||||||
return nil, errors.New("unsupported elliptic curve")
|
|
||||||
}
|
|
||||||
x, y := elliptic.UnmarshalCompressed(namedCurve, pointDER)
|
|
||||||
if x == nil {
|
|
||||||
return nil, errors.New("failed to unmarshal elliptic curve point")
|
|
||||||
}
|
|
||||||
pub := &ecdsa.PublicKey{
|
|
||||||
Curve: namedCurve,
|
|
||||||
X: x,
|
|
||||||
Y: y,
|
|
||||||
}
|
|
||||||
return pub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
|
||||||
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
|
|
||||||
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
|
|
||||||
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
|
|
||||||
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
|
|
||||||
)
|
|
||||||
|
|
||||||
func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
|
|
||||||
switch {
|
|
||||||
case oid.Equal(oidNamedCurveP224):
|
|
||||||
return elliptic.P224()
|
|
||||||
case oid.Equal(oidNamedCurveP256):
|
|
||||||
return elliptic.P256()
|
|
||||||
case oid.Equal(oidNamedCurveP384):
|
|
||||||
return elliptic.P384()
|
|
||||||
case oid.Equal(oidNamedCurveP521):
|
|
||||||
return elliptic.P521()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
"golang.org/x/crypto/cryptobyte/asn1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestECDSA(t *testing.T) {
|
|
||||||
type ASNSignatureTestVector struct {
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment"`
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags"`
|
|
||||||
// The message to sign
|
|
||||||
Msg string `json:"msg"`
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result"`
|
|
||||||
// An ASN.1 encoded signature for msg
|
|
||||||
Sig string `json:"sig"`
|
|
||||||
// Identifier of the test case
|
|
||||||
TcID int `json:"tcId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ECPublicKey struct {
|
|
||||||
// The EC group used by this public key
|
|
||||||
Curve interface{} `json:"curve"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ECDSATestGroup struct {
|
|
||||||
// Unencoded EC public key
|
|
||||||
Key *ECPublicKey `json:"key"`
|
|
||||||
// DER encoded public key
|
|
||||||
KeyDER string `json:"keyDer"`
|
|
||||||
// the hash function used for ECDSA
|
|
||||||
SHA string `json:"sha"`
|
|
||||||
Tests []*ASNSignatureTestVector `json:"tests"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Root struct {
|
|
||||||
TestGroups []*ECDSATestGroup `json:"testGroups"`
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsShouldPass := map[string]bool{
|
|
||||||
// An encoded ASN.1 integer missing a leading zero is invalid, but
|
|
||||||
// accepted by some implementations.
|
|
||||||
"MissingZero": false,
|
|
||||||
// A signature using a weaker hash than the EC params is not a security
|
|
||||||
// risk, as long as the hash is secure.
|
|
||||||
// https://www.imperialviolet.org/2014/05/25/strengthmatching.html
|
|
||||||
"WeakHash": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// supportedCurves is a map of all elliptic curves supported
|
|
||||||
// by crypto/elliptic, which can subsequently be parsed and tested.
|
|
||||||
supportedCurves := map[string]bool{
|
|
||||||
"secp224r1": true,
|
|
||||||
"secp256r1": true,
|
|
||||||
"secp384r1": true,
|
|
||||||
"secp521r1": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, "ecdsa_test.json", &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
curve := tg.Key.Curve.(string)
|
|
||||||
if !supportedCurves[curve] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pub := decodePublicKey(tg.KeyDER).(*ecdsa.PublicKey)
|
|
||||||
h := parseHash(tg.SHA).New()
|
|
||||||
for _, sig := range tg.Tests {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(decodeHex(sig.Msg))
|
|
||||||
hashed := h.Sum(nil)
|
|
||||||
sigBytes := decodeHex(sig.Sig)
|
|
||||||
got := ecdsa.VerifyASN1(pub, hashed, sigBytes)
|
|
||||||
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
|
|
||||||
t.Errorf("tcid: %d, type: %s, comment: %q, VerifyASN1 wanted success: %t", sig.TcID, sig.Result, sig.Comment, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
var r, s big.Int
|
|
||||||
var inner cryptobyte.String
|
|
||||||
input := cryptobyte.String(sigBytes)
|
|
||||||
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
|
|
||||||
!input.Empty() ||
|
|
||||||
!inner.ReadASN1Integer(&r) ||
|
|
||||||
!inner.ReadASN1Integer(&s) ||
|
|
||||||
!inner.Empty() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
got = ecdsa.Verify(pub, hashed, &r, &s)
|
|
||||||
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
|
|
||||||
t.Errorf("tcid: %d, type: %s, comment: %q, Verify wanted success: %t", sig.TcID, sig.Result, sig.Comment, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ed25519"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEddsa(t *testing.T) {
|
|
||||||
// Jwk the private key in webcrypto format
|
|
||||||
type Jwk struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key unencoded key pair
|
|
||||||
type Key struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignatureTestVector
|
|
||||||
type SignatureTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// The message to sign
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// A signature for msg
|
|
||||||
Sig string `json:"sig,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EddsaTestGroup
|
|
||||||
type EddsaTestGroup struct {
|
|
||||||
|
|
||||||
// the private key in webcrypto format
|
|
||||||
Jwk *Jwk `json:"jwk,omitempty"`
|
|
||||||
|
|
||||||
// unencoded key pair
|
|
||||||
Key *Key `json:"key,omitempty"`
|
|
||||||
|
|
||||||
// Asn encoded public key
|
|
||||||
KeyDer string `json:"keyDer,omitempty"`
|
|
||||||
|
|
||||||
// Pem encoded public key
|
|
||||||
KeyPem string `json:"keyPem,omitempty"`
|
|
||||||
Tests []*SignatureTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*EddsaTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, "eddsa_test.json", &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
pub := decodePublicKey(tg.KeyDer).(ed25519.PublicKey)
|
|
||||||
for _, sig := range tg.Tests {
|
|
||||||
got := ed25519.Verify(pub, decodeHex(sig.Msg), decodeHex(sig.Sig))
|
|
||||||
if want := shouldPass(sig.Result, sig.Flags, nil); got != want {
|
|
||||||
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcId, sig.Result, sig.Comment, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/hkdf"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHkdf(t *testing.T) {
|
|
||||||
|
|
||||||
// HkdfTestVector
|
|
||||||
type HkdfTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// the key (input key material)
|
|
||||||
Ikm string `json:"ikm,omitempty"`
|
|
||||||
|
|
||||||
// additional information used in the key derivation
|
|
||||||
Info string `json:"info,omitempty"`
|
|
||||||
|
|
||||||
// the generated bytes (output key material)
|
|
||||||
Okm string `json:"okm,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// the salt for the key derivation
|
|
||||||
Salt string `json:"salt,omitempty"`
|
|
||||||
|
|
||||||
// the size of the output in bytes
|
|
||||||
Size int `json:"size,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// HkdfTestGroup
|
|
||||||
type HkdfTestGroup struct {
|
|
||||||
|
|
||||||
// the size of the ikm in bits
|
|
||||||
KeySize int `json:"keySize,omitempty"`
|
|
||||||
Tests []*HkdfTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*HkdfTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
fileHashAlgorithms := map[string]string{
|
|
||||||
"hkdf_sha1_test.json": "SHA-1",
|
|
||||||
"hkdf_sha256_test.json": "SHA-256",
|
|
||||||
"hkdf_sha384_test.json": "SHA-384",
|
|
||||||
"hkdf_sha512_test.json": "SHA-512",
|
|
||||||
}
|
|
||||||
|
|
||||||
for f := range fileHashAlgorithms {
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, f, &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
for _, tv := range tg.Tests {
|
|
||||||
h := parseHash(fileHashAlgorithms[f]).New
|
|
||||||
hkdf := hkdf.New(h, decodeHex(tv.Ikm), decodeHex(tv.Salt), decodeHex(tv.Info))
|
|
||||||
key := make([]byte, tv.Size)
|
|
||||||
wantPass := shouldPass(tv.Result, tv.Flags, nil)
|
|
||||||
_, err := io.ReadFull(hkdf, key)
|
|
||||||
if (err == nil) != wantPass {
|
|
||||||
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t, got: %v", tv.TcId, tv.Result, tv.Comment, wantPass, err)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
continue // don't validate output text if reading failed
|
|
||||||
}
|
|
||||||
if got, want := key, decodeHex(tv.Okm); !bytes.Equal(got, want) {
|
|
||||||
t.Errorf("tcid: %d, type: %s, comment: %q, output bytes don't match", tv.TcId, tv.Result, tv.Comment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
// Copyright 2020 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 wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHMAC(t *testing.T) {
|
|
||||||
// MacTestVector
|
|
||||||
type MacTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// the key
|
|
||||||
Key string `json:"key,omitempty"`
|
|
||||||
|
|
||||||
// the plaintext
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// the authentication tag
|
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MacTestGroup
|
|
||||||
type MacTestGroup struct {
|
|
||||||
|
|
||||||
// the keySize in bits
|
|
||||||
KeySize int `json:"keySize,omitempty"`
|
|
||||||
|
|
||||||
// the expected size of the tag in bits
|
|
||||||
TagSize int `json:"tagSize,omitempty"`
|
|
||||||
Tests []*MacTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*MacTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
fileHashAlgs := map[string]string{
|
|
||||||
"hmac_sha1_test.json": "SHA-1",
|
|
||||||
"hmac_sha224_test.json": "SHA-224",
|
|
||||||
"hmac_sha256_test.json": "SHA-256",
|
|
||||||
"hmac_sha384_test.json": "SHA-384",
|
|
||||||
"hmac_sha512_test.json": "SHA-512",
|
|
||||||
}
|
|
||||||
|
|
||||||
for f := range fileHashAlgs {
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, f, &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
h := parseHash(fileHashAlgs[f])
|
|
||||||
// Skip test vectors where the tag length does not equal the
|
|
||||||
// hash length, since crypto/hmac does not support generating
|
|
||||||
// these truncated tags.
|
|
||||||
if tg.TagSize/8 != h.Size() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, tv := range tg.Tests {
|
|
||||||
hm := hmac.New(h.New, decodeHex(tv.Key))
|
|
||||||
hm.Write(decodeHex(tv.Msg))
|
|
||||||
tag := hm.Sum(nil)
|
|
||||||
got := hmac.Equal(decodeHex(tv.Tag), tag)
|
|
||||||
if want := shouldPass(tv.Result, tv.Flags, nil); want != got {
|
|
||||||
t.Errorf("%s, tcid: %d, type: %s, comment: %q, unexpected result", f, tv.TcId, tv.Result, tv.Comment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package dsa provides an internal version of dsa.Verify
|
|
||||||
// that is used for the Wycheproof tests.
|
|
||||||
package dsa
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/dsa"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
"golang.org/x/crypto/cryptobyte/asn1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VerifyASN1 verifies the ASN1 encoded signature, sig, of hash using the
|
|
||||||
// public key, pub. Its return value records whether the signature is valid.
|
|
||||||
func VerifyASN1(pub *dsa.PublicKey, hash, sig []byte) bool {
|
|
||||||
var (
|
|
||||||
r, s = &big.Int{}, &big.Int{}
|
|
||||||
inner cryptobyte.String
|
|
||||||
)
|
|
||||||
input := cryptobyte.String(sig)
|
|
||||||
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
|
|
||||||
!input.Empty() ||
|
|
||||||
!inner.ReadASN1Integer(r) ||
|
|
||||||
!inner.ReadASN1Integer(s) ||
|
|
||||||
!inner.Empty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return dsa.Verify(pub, hash, r, s)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright 2022 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 !boringcrypto
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
const boringcryptoEnabled = false
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
// Copyright 2020 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 wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRSAOAEPDecrypt(t *testing.T) {
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// RsaesOaepTestVector
|
|
||||||
type RsaesOaepTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// An encryption of msg
|
|
||||||
Ct string `json:"ct,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// The label used for the encryption
|
|
||||||
Label string `json:"label,omitempty"`
|
|
||||||
|
|
||||||
// The encrypted message
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RsaesOaepTestGroup
|
|
||||||
type RsaesOaepTestGroup struct {
|
|
||||||
|
|
||||||
// The private exponent
|
|
||||||
D string `json:"d,omitempty"`
|
|
||||||
|
|
||||||
// The public exponent
|
|
||||||
E string `json:"e,omitempty"`
|
|
||||||
|
|
||||||
// the message generating function (e.g. MGF1)
|
|
||||||
Mgf string `json:"mgf,omitempty"`
|
|
||||||
|
|
||||||
// The hash function used for the message generating function.
|
|
||||||
MgfSha string `json:"mgfSha,omitempty"`
|
|
||||||
|
|
||||||
// The modulus of the key
|
|
||||||
N string `json:"n,omitempty"`
|
|
||||||
|
|
||||||
// Pem encoded private key
|
|
||||||
PrivateKeyPem string `json:"privateKeyPem,omitempty"`
|
|
||||||
|
|
||||||
// Pkcs 8 encoded private key.
|
|
||||||
PrivateKeyPkcs8 string `json:"privateKeyPkcs8,omitempty"`
|
|
||||||
|
|
||||||
// The hash function for hashing the label.
|
|
||||||
Sha string `json:"sha,omitempty"`
|
|
||||||
Tests []*RsaesOaepTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*RsaesOaepTestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// rsa.DecryptOAEP doesn't support using a different hash for the
|
|
||||||
// MGF and the label, so skip all of the test vectors that use
|
|
||||||
// these unbalanced constructions. rsa_oaep_misc_test.json contains
|
|
||||||
// both balanced and unbalanced constructions so in that case
|
|
||||||
// we just filter out any test groups where MgfSha != Sha
|
|
||||||
files := []string{
|
|
||||||
"rsa_oaep_2048_sha1_mgf1sha1_test.json",
|
|
||||||
"rsa_oaep_2048_sha224_mgf1sha224_test.json",
|
|
||||||
"rsa_oaep_2048_sha256_mgf1sha256_test.json",
|
|
||||||
"rsa_oaep_2048_sha384_mgf1sha384_test.json",
|
|
||||||
"rsa_oaep_2048_sha512_mgf1sha512_test.json",
|
|
||||||
"rsa_oaep_3072_sha256_mgf1sha256_test.json",
|
|
||||||
"rsa_oaep_3072_sha512_mgf1sha512_test.json",
|
|
||||||
"rsa_oaep_4096_sha256_mgf1sha256_test.json",
|
|
||||||
"rsa_oaep_4096_sha512_mgf1sha512_test.json",
|
|
||||||
"rsa_oaep_misc_test.json",
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsShouldPass := map[string]bool{
|
|
||||||
// rsa.DecryptOAEP happily supports small key sizes
|
|
||||||
"SmallModulus": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range files {
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, f, &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
if tg.MgfSha != tg.Sha {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
priv, err := x509.ParsePKCS8PrivateKey(decodeHex(tg.PrivateKeyPkcs8))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%s failed to parse PKCS #8 private key: %s", f, err)
|
|
||||||
}
|
|
||||||
hash := parseHash(tg.Sha)
|
|
||||||
for _, tv := range tg.Tests {
|
|
||||||
t.Run(fmt.Sprintf("%s #%d", f, tv.TcId), func(t *testing.T) {
|
|
||||||
wantPass := shouldPass(tv.Result, tv.Flags, flagsShouldPass)
|
|
||||||
plaintext, err := rsa.DecryptOAEP(hash.New(), nil, priv.(*rsa.PrivateKey), decodeHex(tv.Ct), decodeHex(tv.Label))
|
|
||||||
if wantPass {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("comment: %s, expected success: %s", tv.Comment, err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(plaintext, decodeHex(tv.Msg)) {
|
|
||||||
t.Errorf("comment: %s, unexpected plaintext: got %x, want %s", tv.Comment, plaintext, tv.Msg)
|
|
||||||
}
|
|
||||||
} else if err == nil {
|
|
||||||
t.Errorf("comment: %s, expected failure", tv.Comment)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRsaPss(t *testing.T) {
|
|
||||||
// KeyJwk Public key in JWK format
|
|
||||||
type KeyJwk struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignatureTestVector
|
|
||||||
type SignatureTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// The message to sign
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// A signature for msg
|
|
||||||
Sig string `json:"sig,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RsassaPkcs1TestGroup
|
|
||||||
type RsassaPkcs1TestGroup struct {
|
|
||||||
|
|
||||||
// The private exponent
|
|
||||||
D string `json:"d,omitempty"`
|
|
||||||
|
|
||||||
// The public exponent
|
|
||||||
E string `json:"e,omitempty"`
|
|
||||||
|
|
||||||
// ASN encoding of the sequence [n, e]
|
|
||||||
KeyAsn string `json:"keyAsn,omitempty"`
|
|
||||||
|
|
||||||
// ASN encoding of the public key
|
|
||||||
KeyDer string `json:"keyDer,omitempty"`
|
|
||||||
|
|
||||||
// Public key in JWK format
|
|
||||||
KeyJwk *KeyJwk `json:"keyJwk,omitempty"`
|
|
||||||
|
|
||||||
// Pem encoded public key
|
|
||||||
KeyPem string `json:"keyPem,omitempty"`
|
|
||||||
|
|
||||||
// the size of the modulus in bits
|
|
||||||
KeySize int `json:"keySize,omitempty"`
|
|
||||||
|
|
||||||
// The modulus of the key
|
|
||||||
N string `json:"n,omitempty"`
|
|
||||||
|
|
||||||
// The salt length
|
|
||||||
SLen int `json:"sLen,omitempty"`
|
|
||||||
|
|
||||||
// the hash function used for the message
|
|
||||||
Sha string `json:"sha,omitempty"`
|
|
||||||
Tests []*SignatureTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*RsassaPkcs1TestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsShouldPass := map[string]bool{
|
|
||||||
// A signature using a weaker hash than the EC params is not a security risk, as long as the hash is secure.
|
|
||||||
// https://www.imperialviolet.org/2014/05/25/strengthmatching.html
|
|
||||||
"WeakHash": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// filesOverrideToPassZeroSLen is a map of all test files
|
|
||||||
// and which TcIds that should be overridden to pass if the
|
|
||||||
// rsa.PSSOptions.SaltLength is zero.
|
|
||||||
// These tests expect a failure with a PSSOptions.SaltLength: 0
|
|
||||||
// and a signature that uses a different salt length. However,
|
|
||||||
// a salt length of 0 is defined as rsa.PSSSaltLengthAuto which
|
|
||||||
// works deterministically to auto-detect the length when
|
|
||||||
// verifying, so these tests actually pass as they should.
|
|
||||||
filesOverrideToPassZeroSLen := map[string][]int{
|
|
||||||
"rsa_pss_2048_sha1_mgf1_20_test.json": []int{46, 47},
|
|
||||||
"rsa_pss_2048_sha256_mgf1_0_test.json": []int{67, 68},
|
|
||||||
"rsa_pss_2048_sha256_mgf1_32_test.json": []int{67, 68},
|
|
||||||
"rsa_pss_3072_sha256_mgf1_32_test.json": []int{67, 68},
|
|
||||||
"rsa_pss_4096_sha256_mgf1_32_test.json": []int{67, 68},
|
|
||||||
"rsa_pss_4096_sha512_mgf1_32_test.json": []int{136, 137},
|
|
||||||
// "rsa_pss_misc_test.json": nil, // TODO: This ones seems to be broken right now, but can enable later on.
|
|
||||||
}
|
|
||||||
|
|
||||||
if !boringcryptoEnabled {
|
|
||||||
// boringcrypto doesn't support the truncated SHA-512 hashes, so only
|
|
||||||
// test them if boringcrypto isn't enabled.
|
|
||||||
filesOverrideToPassZeroSLen["rsa_pss_2048_sha512_256_mgf1_28_test.json"] = []int{13, 14, 15}
|
|
||||||
filesOverrideToPassZeroSLen["rsa_pss_2048_sha512_256_mgf1_32_test.json"] = []int{13, 14}
|
|
||||||
}
|
|
||||||
|
|
||||||
for f := range filesOverrideToPassZeroSLen {
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, f, &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
pub := decodePublicKey(tg.KeyDer).(*rsa.PublicKey)
|
|
||||||
ch := parseHash(tg.Sha)
|
|
||||||
h := ch.New()
|
|
||||||
opts := &rsa.PSSOptions{
|
|
||||||
Hash: ch,
|
|
||||||
SaltLength: rsa.PSSSaltLengthAuto,
|
|
||||||
}
|
|
||||||
// Run all the tests twice: the first time with the salt length
|
|
||||||
// as PSSSaltLengthAuto, and the second time with the salt length
|
|
||||||
// explictily set to tg.SLen.
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
for _, sig := range tg.Tests {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(decodeHex(sig.Msg))
|
|
||||||
hashed := h.Sum(nil)
|
|
||||||
err := rsa.VerifyPSS(pub, ch, hashed, decodeHex(sig.Sig), opts)
|
|
||||||
want := shouldPass(sig.Result, sig.Flags, flagsShouldPass)
|
|
||||||
if opts.SaltLength == 0 {
|
|
||||||
for _, id := range filesOverrideToPassZeroSLen[f] {
|
|
||||||
if sig.TcId == id {
|
|
||||||
want = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (err == nil) != want {
|
|
||||||
t.Errorf("file: %v, tcid: %d, type: %s, opts.SaltLength: %v, comment: %q, wanted success: %t", f, sig.TcId, sig.Result, opts.SaltLength, sig.Comment, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Update opts.SaltLength for the second run of the tests.
|
|
||||||
opts.SaltLength = tg.SLen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRsa(t *testing.T) {
|
|
||||||
// KeyJwk Public key in JWK format
|
|
||||||
type KeyJwk struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notes a description of the labels used in the test vectors
|
|
||||||
type Notes struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignatureTestVector
|
|
||||||
type SignatureTestVector struct {
|
|
||||||
|
|
||||||
// A brief description of the test case
|
|
||||||
Comment string `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// A list of flags
|
|
||||||
Flags []string `json:"flags,omitempty"`
|
|
||||||
|
|
||||||
// The message to sign
|
|
||||||
Msg string `json:"msg,omitempty"`
|
|
||||||
|
|
||||||
// Test result
|
|
||||||
Result string `json:"result,omitempty"`
|
|
||||||
|
|
||||||
// A signature for msg
|
|
||||||
Sig string `json:"sig,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the test case
|
|
||||||
TcId int `json:"tcId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RsassaPkcs1TestGroup
|
|
||||||
type RsassaPkcs1TestGroup struct {
|
|
||||||
|
|
||||||
// The private exponent
|
|
||||||
D string `json:"d,omitempty"`
|
|
||||||
|
|
||||||
// The public exponent
|
|
||||||
E string `json:"e,omitempty"`
|
|
||||||
|
|
||||||
// ASN encoding of the sequence [n, e]
|
|
||||||
KeyAsn string `json:"keyAsn,omitempty"`
|
|
||||||
|
|
||||||
// ASN encoding of the public key
|
|
||||||
KeyDer string `json:"keyDer,omitempty"`
|
|
||||||
|
|
||||||
// Public key in JWK format
|
|
||||||
KeyJwk *KeyJwk `json:"keyJwk,omitempty"`
|
|
||||||
|
|
||||||
// Pem encoded public key
|
|
||||||
KeyPem string `json:"keyPem,omitempty"`
|
|
||||||
|
|
||||||
// the size of the modulus in bits
|
|
||||||
KeySize int `json:"keySize,omitempty"`
|
|
||||||
|
|
||||||
// The modulus of the key
|
|
||||||
N string `json:"n,omitempty"`
|
|
||||||
|
|
||||||
// the hash function used for the message
|
|
||||||
Sha string `json:"sha,omitempty"`
|
|
||||||
Tests []*SignatureTestVector `json:"tests,omitempty"`
|
|
||||||
Type interface{} `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root
|
|
||||||
type Root struct {
|
|
||||||
|
|
||||||
// the primitive tested in the test file
|
|
||||||
Algorithm string `json:"algorithm,omitempty"`
|
|
||||||
|
|
||||||
// the version of the test vectors.
|
|
||||||
GeneratorVersion string `json:"generatorVersion,omitempty"`
|
|
||||||
|
|
||||||
// additional documentation
|
|
||||||
Header []string `json:"header,omitempty"`
|
|
||||||
|
|
||||||
// a description of the labels used in the test vectors
|
|
||||||
Notes *Notes `json:"notes,omitempty"`
|
|
||||||
|
|
||||||
// the number of test vectors in this test
|
|
||||||
NumberOfTests int `json:"numberOfTests,omitempty"`
|
|
||||||
Schema interface{} `json:"schema,omitempty"`
|
|
||||||
TestGroups []*RsassaPkcs1TestGroup `json:"testGroups,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
flagsShouldPass := map[string]bool{
|
|
||||||
// Omitting the parameter field in an ASN encoded integer is a legacy behavior.
|
|
||||||
"MissingNull": false,
|
|
||||||
// Keys with a modulus less than 2048 bits are supported by crypto/rsa.
|
|
||||||
"SmallModulus": true,
|
|
||||||
// Small public keys are supported by crypto/rsa.
|
|
||||||
"SmallPublicKey": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var root Root
|
|
||||||
readTestVector(t, "rsa_signature_test.json", &root)
|
|
||||||
for _, tg := range root.TestGroups {
|
|
||||||
pub := decodePublicKey(tg.KeyDer).(*rsa.PublicKey)
|
|
||||||
ch := parseHash(tg.Sha)
|
|
||||||
h := ch.New()
|
|
||||||
for _, sig := range tg.Tests {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(decodeHex(sig.Msg))
|
|
||||||
hashed := h.Sum(nil)
|
|
||||||
err := rsa.VerifyPKCS1v15(pub, ch, hashed, decodeHex(sig.Sig))
|
|
||||||
want := shouldPass(sig.Result, sig.Flags, flagsShouldPass)
|
|
||||||
if (err == nil) != want {
|
|
||||||
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcId, sig.Result, sig.Comment, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package wycheproof runs a set of the Wycheproof tests
|
|
||||||
// provided by https://github.com/google/wycheproof.
|
|
||||||
package wycheproof
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
_ "crypto/sha1"
|
|
||||||
_ "crypto/sha256"
|
|
||||||
_ "crypto/sha512"
|
|
||||||
)
|
|
||||||
|
|
||||||
const wycheproofModVer = "v0.0.0-20191219022705-2196000605e4"
|
|
||||||
|
|
||||||
var wycheproofTestVectorsDir string
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
flag.Parse()
|
|
||||||
if flag.Lookup("test.short").Value.(flag.Getter).Get().(bool) {
|
|
||||||
log.Println("skipping test that downloads testdata via 'go mod download' in short mode")
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
if _, err := exec.LookPath("go"); err != nil {
|
|
||||||
log.Printf("skipping test because 'go' command is unavailable: %v", err)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
if os.Getenv("GO_BUILDER_FLAKY_NET") != "" {
|
|
||||||
log.Printf("skipping test because GO_BUILDER_FLAKY_NET is set")
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download the JSON test files from github.com/google/wycheproof
|
|
||||||
// using `go mod download -json` so the cached source of the testdata
|
|
||||||
// can be used in the following tests.
|
|
||||||
path := "github.com/google/wycheproof@" + wycheproofModVer
|
|
||||||
cmd := exec.Command("go", "mod", "download", "-json", path)
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output)
|
|
||||||
}
|
|
||||||
var dm struct {
|
|
||||||
Dir string // absolute path to cached source root directory
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(output, &dm); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
// Now that the module has been downloaded, use the absolute path of the
|
|
||||||
// cached source as the root directory for all tests going forward.
|
|
||||||
wycheproofTestVectorsDir = filepath.Join(dm.Dir, "testvectors")
|
|
||||||
os.Exit(m.Run())
|
|
||||||
}
|
|
||||||
|
|
||||||
func readTestVector(t *testing.T, f string, dest interface{}) {
|
|
||||||
b, err := os.ReadFile(filepath.Join(wycheproofTestVectorsDir, f))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to read json file: %v", err)
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(b, &dest); err != nil {
|
|
||||||
t.Fatalf("failed to unmarshal json file: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeHex(s string) []byte {
|
|
||||||
b, err := hex.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodePublicKey(der string) interface{} {
|
|
||||||
d := decodeHex(der)
|
|
||||||
pub, err := x509.ParsePKIXPublicKey(d)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("failed to parse DER encoded public key: %v", err))
|
|
||||||
}
|
|
||||||
return pub
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseHash(h string) crypto.Hash {
|
|
||||||
switch h {
|
|
||||||
case "SHA-1":
|
|
||||||
return crypto.SHA1
|
|
||||||
case "SHA-256":
|
|
||||||
return crypto.SHA256
|
|
||||||
case "SHA-224":
|
|
||||||
return crypto.SHA224
|
|
||||||
case "SHA-384":
|
|
||||||
return crypto.SHA384
|
|
||||||
case "SHA-512":
|
|
||||||
return crypto.SHA512
|
|
||||||
case "SHA-512/224":
|
|
||||||
return crypto.SHA512_224
|
|
||||||
case "SHA-512/256":
|
|
||||||
return crypto.SHA512_256
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("could not identify SHA hash algorithm: %q", h))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// shouldPass returns whether or not the test should pass.
|
|
||||||
// flagsShouldPass is a map associated with whether or not
|
|
||||||
// a flag for an "acceptable" result should pass.
|
|
||||||
// Every possible flag value that's associated with an
|
|
||||||
// "acceptable" result should be explicitly specified,
|
|
||||||
// otherwise the test will panic.
|
|
||||||
func shouldPass(result string, flags []string, flagsShouldPass map[string]bool) bool {
|
|
||||||
switch result {
|
|
||||||
case "valid":
|
|
||||||
return true
|
|
||||||
case "invalid":
|
|
||||||
return false
|
|
||||||
case "acceptable":
|
|
||||||
for _, flag := range flags {
|
|
||||||
pass, ok := flagsShouldPass[flag]
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Sprintf("unspecified flag: %q", flag))
|
|
||||||
}
|
|
||||||
if !pass {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true // There are no flags, or all are meant to pass.
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unexpected result: %v", result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+10
-19
@@ -2,25 +2,16 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
/*
|
// Package auth authenticates a message using a secret key.
|
||||||
Package auth authenticates a message using a secret key.
|
//
|
||||||
|
// This package is interoperable with [NaCl].
|
||||||
The Sum function, viewed as a function of the message for a uniform random
|
//
|
||||||
key, is designed to meet the standard notion of unforgeability. This means
|
// The auth package is essentially a wrapper for HMAC-SHA-512 (implemented by
|
||||||
that an attacker cannot find authenticators for any messages not authenticated
|
// crypto/hmac and crypto/sha512), truncated to 32 bytes. It is [frozen] and is
|
||||||
by the sender, even if the attacker has adaptively influenced the messages
|
// not accepting new features.
|
||||||
authenticated by the sender. For a formal definition see, e.g., Section 2.4
|
//
|
||||||
of Bellare, Kilian, and Rogaway, "The security of the cipher block chaining
|
// [NaCl]: https://nacl.cr.yp.to/auth.html
|
||||||
message authentication code," Journal of Computer and System Sciences 61 (2000),
|
// [frozen]: https://go.dev/wiki/Frozen
|
||||||
362–399; http://www-cse.ucsd.edu/~mihir/papers/cbc.html.
|
|
||||||
|
|
||||||
auth does not make any promises regarding "strong" unforgeability; perhaps
|
|
||||||
one valid authenticator can be converted into another valid authenticator for
|
|
||||||
the same message. NaCl also does not make any promises regarding "truncated
|
|
||||||
unforgeability."
|
|
||||||
|
|
||||||
This package is interoperable with NaCl: https://nacl.cr.yp.to/auth.html.
|
|
||||||
*/
|
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func TestSealOpenAnonymous(t *testing.T) {
|
|||||||
t.Fatal("expected out to be unchanged")
|
t.Fatal("expected out to be unchanged")
|
||||||
}
|
}
|
||||||
if !bytes.HasPrefix(box, orig) {
|
if !bytes.HasPrefix(box, orig) {
|
||||||
t.Fatal("expected out to be coppied to returned slice")
|
t.Fatal("expected out to be copied to returned slice")
|
||||||
}
|
}
|
||||||
_, ok = OpenAnonymous(nil, box[len(out):], publicKey, privateKey)
|
_, ok = OpenAnonymous(nil, box[len(out):], publicKey, privateKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
+8
-13
@@ -4,20 +4,15 @@
|
|||||||
|
|
||||||
// Package sign signs small messages using public-key cryptography.
|
// Package sign signs small messages using public-key cryptography.
|
||||||
//
|
//
|
||||||
// Sign uses Ed25519 to sign messages. The length of messages is not hidden.
|
// This package is interoperable with [libsodium], as well as [TweetNaCl].
|
||||||
// Messages should be small because:
|
|
||||||
// 1. The whole message needs to be held in memory to be processed.
|
|
||||||
// 2. Using large messages pressures implementations on small machines to process
|
|
||||||
// plaintext without verifying the signature. This is very dangerous, and this API
|
|
||||||
// discourages it, but a protocol that uses excessive message sizes might present
|
|
||||||
// some implementations with no other choice.
|
|
||||||
// 3. Performance may be improved by working with messages that fit into data caches.
|
|
||||||
// Thus large amounts of data should be chunked so that each message is small.
|
|
||||||
//
|
//
|
||||||
// This package is not interoperable with the current release of NaCl
|
// The sign package is essentially a wrapper for the Ed25519 signature
|
||||||
// (https://nacl.cr.yp.to/sign.html), which does not support Ed25519 yet. However,
|
// algorithm (implemented by crypto/ed25519). It is [frozen] and is not accepting
|
||||||
// it is compatible with the NaCl fork libsodium (https://www.libsodium.org), as well
|
// new features.
|
||||||
// as TweetNaCl (https://tweetnacl.cr.yp.to/).
|
//
|
||||||
|
// [libsodium]: https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures
|
||||||
|
// [TweetNaCl]: https://tweetnacl.cr.yp.to/
|
||||||
|
// [frozen]: https://go.dev/wiki/Frozen
|
||||||
package sign
|
package sign
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -69,6 +69,6 @@ func TestGenerateSignOpen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(message, testMessage) {
|
if !bytes.Equal(message, testMessage) {
|
||||||
t.Fatalf("verified message does not match signed messge, got\n%x\n, expected\n%x", message, testMessage)
|
t.Fatalf("verified message does not match signed message, got\n%x\n, expected\n%x", message, testMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-5
@@ -85,7 +85,8 @@ type certID struct {
|
|||||||
|
|
||||||
// https://tools.ietf.org/html/rfc2560#section-4.1.1
|
// https://tools.ietf.org/html/rfc2560#section-4.1.1
|
||||||
type ocspRequest struct {
|
type ocspRequest struct {
|
||||||
TBSRequest tbsRequest
|
TBSRequest tbsRequest
|
||||||
|
OptionalSignature asn1.RawValue `asn1:"explicit,tag:0,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type tbsRequest struct {
|
type tbsRequest struct {
|
||||||
@@ -321,10 +322,10 @@ type Request struct {
|
|||||||
func (req *Request) Marshal() ([]byte, error) {
|
func (req *Request) Marshal() ([]byte, error) {
|
||||||
hashAlg := getOIDFromHashAlgorithm(req.HashAlgorithm)
|
hashAlg := getOIDFromHashAlgorithm(req.HashAlgorithm)
|
||||||
if hashAlg == nil {
|
if hashAlg == nil {
|
||||||
return nil, errors.New("Unknown hash algorithm")
|
return nil, errors.New("unknown hash algorithm")
|
||||||
}
|
}
|
||||||
return asn1.Marshal(ocspRequest{
|
return asn1.Marshal(ocspRequest{
|
||||||
tbsRequest{
|
TBSRequest: tbsRequest{
|
||||||
Version: 0,
|
Version: 0,
|
||||||
RequestList: []request{
|
RequestList: []request{
|
||||||
{
|
{
|
||||||
@@ -418,8 +419,10 @@ func (p ParseError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseRequest parses an OCSP request in DER form. It only supports
|
// ParseRequest parses an OCSP request in DER form. It only supports
|
||||||
// requests for a single certificate. Signed requests are not supported.
|
// requests for a single certificate identifier. If a request includes
|
||||||
// If a request includes a signature, it will result in a ParseError.
|
// multiple certificate identifiers, only the first will be included in
|
||||||
|
// the parsed Request. Signed requests are not supported. If a request
|
||||||
|
// includes a signature, it will result in a ParseError.
|
||||||
func ParseRequest(bytes []byte) (*Request, error) {
|
func ParseRequest(bytes []byte) (*Request, error) {
|
||||||
var req ocspRequest
|
var req ocspRequest
|
||||||
rest, err := asn1.Unmarshal(bytes, &req)
|
rest, err := asn1.Unmarshal(bytes, &req)
|
||||||
@@ -430,6 +433,10 @@ func ParseRequest(bytes []byte) (*Request, error) {
|
|||||||
return nil, ParseError("trailing data in OCSP request")
|
return nil, ParseError("trailing data in OCSP request")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(req.OptionalSignature.FullBytes) > 0 {
|
||||||
|
return nil, ParseError("signed OCSP requests are not supported")
|
||||||
|
}
|
||||||
|
|
||||||
if len(req.TBSRequest.RequestList) == 0 {
|
if len(req.TBSRequest.RequestList) == 0 {
|
||||||
return nil, ParseError("OCSP request contains no request body")
|
return nil, ParseError("OCSP request contains no request body")
|
||||||
}
|
}
|
||||||
|
|||||||
+127
-1
@@ -15,10 +15,15 @@ import (
|
|||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOCSPDecode(t *testing.T) {
|
func TestOCSPDecode(t *testing.T) {
|
||||||
@@ -190,7 +195,7 @@ func TestOCSPRequest(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Compare(expectedBytes, marshaledRequest) != 0 {
|
if !bytes.Equal(expectedBytes, marshaledRequest) {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"Marshaled request doesn't match expected: wanted %x, got %x",
|
"Marshaled request doesn't match expected: wanted %x, got %x",
|
||||||
expectedBytes,
|
expectedBytes,
|
||||||
@@ -199,6 +204,127 @@ func TestOCSPRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOCSPRequestErrors(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "signed request",
|
||||||
|
input: testOCSPRequest(1, true),
|
||||||
|
wantErr: "signed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trailing data",
|
||||||
|
input: append(testOCSPRequest(1, false), 0x00),
|
||||||
|
wantErr: "trailing",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
_, err := ParseRequest(tc.input)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf(
|
||||||
|
"ParseRequest succeeded, want ParseError containing %q",
|
||||||
|
tc.wantErr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var parseError ParseError
|
||||||
|
if !errors.As(err, &parseError) {
|
||||||
|
t.Fatalf("ParseRequest returned %T, want ParseError", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(parseError.Error(), tc.wantErr) {
|
||||||
|
t.Fatalf(
|
||||||
|
"ParseRequest error = %q, want it to contain %q",
|
||||||
|
parseError.Error(), tc.wantErr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOCSPMultipleCertRequest(t *testing.T) {
|
||||||
|
req, err := ParseRequest(testOCSPRequest(2, false))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseRequest errored for multiple request input: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testOCSPRequest uses the request cert counter as the issuerNameHash,
|
||||||
|
// issuerKeyHash and serial. We expect the first request cert to be used
|
||||||
|
// (counter=1), not the second (counter=2).
|
||||||
|
wantHash := []byte{0x01}
|
||||||
|
if actual := req.IssuerNameHash; !bytes.Equal(actual, wantHash) {
|
||||||
|
t.Errorf(
|
||||||
|
"ParseRequest.IssuerNameHash returned = %q, wanted %q",
|
||||||
|
actual, wantHash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if actual := req.IssuerKeyHash; !bytes.Equal(actual, wantHash) {
|
||||||
|
t.Errorf("ParseRequest.IssuerKeyHash returned = %q, wanted %q",
|
||||||
|
actual, wantHash)
|
||||||
|
}
|
||||||
|
wantSerial := big.NewInt(0x01)
|
||||||
|
if actual := req.SerialNumber; actual.Cmp(wantSerial) != 0 {
|
||||||
|
t.Errorf("ParseRequest.SerialNumber returned = %q, wanted %q",
|
||||||
|
actual, wantHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testOCSPRequest constructs an unsupported OCSP request in DER form for testing.
|
||||||
|
//
|
||||||
|
// We build these by hand using cryptobyte to specifically target shapes that are
|
||||||
|
// not supported by ParseRequest, and can't be created with CreateRequest.
|
||||||
|
//
|
||||||
|
// The resulting requestList will contain numCerts RequestList entries. If
|
||||||
|
// includeSignature is true, the request will carry a signature field (with an
|
||||||
|
// arbitrary signatureAlgorithm and signature).
|
||||||
|
//
|
||||||
|
// See RFC 6960, §4.1.1.
|
||||||
|
func testOCSPRequest(numCerts int, includeSignature bool) []byte {
|
||||||
|
sha1AlgID, err := asn1.Marshal(pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26},
|
||||||
|
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b cryptobyte.Builder
|
||||||
|
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) { // OCSPRequest
|
||||||
|
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) { // tbsRequest
|
||||||
|
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) { // requestList
|
||||||
|
for i := 0; i < numCerts; i++ {
|
||||||
|
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) { // Request
|
||||||
|
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) { // CertID
|
||||||
|
b.AddBytes(sha1AlgID) // hashAlgorithm
|
||||||
|
b.AddASN1OctetString([]byte{byte(i + 1)}) // issuerNameHash
|
||||||
|
b.AddASN1OctetString([]byte{byte(i + 1)}) // issuerKeyHash
|
||||||
|
b.AddASN1Int64(int64(i + 1)) // serialNumber
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if includeSignature {
|
||||||
|
// optionalSignature [0] EXPLICIT Signature
|
||||||
|
b.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
|
||||||
|
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) { // Signature
|
||||||
|
b.AddBytes(sha1AlgID) // signatureAlgorithm
|
||||||
|
b.AddASN1BitString([]byte{0x09, 0x0A, 0x0B, 0x0C}) // signature
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
der, err := b.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return der
|
||||||
|
}
|
||||||
|
|
||||||
func TestOCSPResponse(t *testing.T) {
|
func TestOCSPResponse(t *testing.T) {
|
||||||
leafCert, _ := hex.DecodeString(leafCertHex)
|
leafCert, _ := hex.DecodeString(leafCertHex)
|
||||||
leaf, err := x509.ParseCertificate(leafCert)
|
leaf, err := x509.ParseCertificate(leafCert)
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
|
// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
|
||||||
// very similar to PEM except that it has an additional CRC checksum.
|
// very similar to PEM except that it has an additional CRC checksum.
|
||||||
//
|
//
|
||||||
// Deprecated: this package is unmaintained except for security fixes. New
|
// Deprecated: this package is unsafe by design, and has numerous known security
|
||||||
// applications should consider a more focused, modern alternative to OpenPGP
|
// issues. It is not maintained, and should not be used. New applications should
|
||||||
// for their specific task. If you are required to interoperate with OpenPGP
|
// consider a more focused, modern alternative to OpenPGP for their specific
|
||||||
// systems and need a maintained package, consider a community fork.
|
// task. If you are required to interoperate with OpenPGP systems and need a
|
||||||
// See https://golang.org/issue/44226.
|
// maintained package, consider github.com/ProtonMail/go-crypto/openpgp, which
|
||||||
|
// is a maintained fork that aims to be a drop-in replacement for this package.
|
||||||
package armor
|
package armor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
// Clearsigned messages are cryptographically signed, but the contents of the
|
// Clearsigned messages are cryptographically signed, but the contents of the
|
||||||
// message are kept in plaintext so that it can be read without special tools.
|
// message are kept in plaintext so that it can be read without special tools.
|
||||||
//
|
//
|
||||||
// Deprecated: this package is unmaintained except for security fixes. New
|
// Deprecated: this package is unsafe by design, and has numerous known security
|
||||||
// applications should consider a more focused, modern alternative to OpenPGP
|
// issues. It is not maintained, and should not be used. New applications should
|
||||||
// for their specific task. If you are required to interoperate with OpenPGP
|
// consider a more focused, modern alternative to OpenPGP for their specific
|
||||||
// systems and need a maintained package, consider a community fork.
|
// task. If you are required to interoperate with OpenPGP systems and need a
|
||||||
// See https://golang.org/issue/44226.
|
// maintained package, consider github.com/ProtonMail/go-crypto/openpgp, which
|
||||||
|
// is a maintained fork that aims to be a drop-in replacement for this package.
|
||||||
package clearsign
|
package clearsign
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
// Package errors contains common error types for the OpenPGP packages.
|
// Package errors contains common error types for the OpenPGP packages.
|
||||||
//
|
//
|
||||||
// Deprecated: this package is unmaintained except for security fixes. New
|
// Deprecated: this package is unsafe by design, and has numerous known security
|
||||||
// applications should consider a more focused, modern alternative to OpenPGP
|
// issues. It is not maintained, and should not be used. New applications should
|
||||||
// for their specific task. If you are required to interoperate with OpenPGP
|
// consider a more focused, modern alternative to OpenPGP for their specific
|
||||||
// systems and need a maintained package, consider a community fork.
|
// task. If you are required to interoperate with OpenPGP systems and need a
|
||||||
// See https://golang.org/issue/44226.
|
// maintained package, consider github.com/ProtonMail/go-crypto/openpgp, which
|
||||||
|
// is a maintained fork that aims to be a drop-in replacement for this package.
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func TestOpaqueParseReason(t *testing.T) {
|
|||||||
const expectedBad = 3
|
const expectedBad = 3
|
||||||
// Test post-conditions, make sure we actually parsed packets as expected.
|
// Test post-conditions, make sure we actually parsed packets as expected.
|
||||||
if badPackets != expectedBad {
|
if badPackets != expectedBad {
|
||||||
t.Errorf("unexpected # unparseable packets: %d (want %d)", badPackets, expectedBad)
|
t.Errorf("unexpected # unparsable packets: %d (want %d)", badPackets, expectedBad)
|
||||||
}
|
}
|
||||||
if uid == nil {
|
if uid == nil {
|
||||||
t.Errorf("failed to find expected UID in unsupported keyring")
|
t.Errorf("failed to find expected UID in unsupported keyring")
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
// Package packet implements parsing and serialization of OpenPGP packets, as
|
// Package packet implements parsing and serialization of OpenPGP packets, as
|
||||||
// specified in RFC 4880.
|
// specified in RFC 4880.
|
||||||
//
|
//
|
||||||
// Deprecated: this package is unmaintained except for security fixes. New
|
// Deprecated: this package is unsafe by design, and has numerous known security
|
||||||
// applications should consider a more focused, modern alternative to OpenPGP
|
// issues. It is not maintained, and should not be used. New applications should
|
||||||
// for their specific task. If you are required to interoperate with OpenPGP
|
// consider a more focused, modern alternative to OpenPGP for their specific
|
||||||
// systems and need a maintained package, consider a community fork.
|
// task. If you are required to interoperate with OpenPGP systems and need a
|
||||||
// See https://golang.org/issue/44226.
|
// maintained package, consider github.com/ProtonMail/go-crypto/openpgp, which
|
||||||
|
// is a maintained fork that aims to be a drop-in replacement for this package.
|
||||||
package packet
|
package packet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
+6
-5
@@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
// Package openpgp implements high level operations on OpenPGP messages.
|
// Package openpgp implements high level operations on OpenPGP messages.
|
||||||
//
|
//
|
||||||
// Deprecated: this package is unmaintained except for security fixes. New
|
// Deprecated: this package is unsafe by design, and has numerous known security
|
||||||
// applications should consider a more focused, modern alternative to OpenPGP
|
// issues. It is not maintained, and should not be used. New applications should
|
||||||
// for their specific task. If you are required to interoperate with OpenPGP
|
// consider a more focused, modern alternative to OpenPGP for their specific
|
||||||
// systems and need a maintained package, consider a community fork.
|
// task. If you are required to interoperate with OpenPGP systems and need a
|
||||||
// See https://golang.org/issue/44226.
|
// maintained package, consider github.com/ProtonMail/go-crypto/openpgp, which
|
||||||
|
// is a maintained fork that aims to be a drop-in replacement for this package.
|
||||||
package openpgp
|
package openpgp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
+7
-6
@@ -5,11 +5,12 @@
|
|||||||
// Package s2k implements the various OpenPGP string-to-key transforms as
|
// Package s2k implements the various OpenPGP string-to-key transforms as
|
||||||
// specified in RFC 4800 section 3.7.1.
|
// specified in RFC 4800 section 3.7.1.
|
||||||
//
|
//
|
||||||
// Deprecated: this package is unmaintained except for security fixes. New
|
// Deprecated: this package is unsafe by design, and has numerous known security
|
||||||
// applications should consider a more focused, modern alternative to OpenPGP
|
// issues. It is not maintained, and should not be used. New applications should
|
||||||
// for their specific task. If you are required to interoperate with OpenPGP
|
// consider a more focused, modern alternative to OpenPGP for their specific
|
||||||
// systems and need a maintained package, consider a community fork.
|
// task. If you are required to interoperate with OpenPGP systems and need a
|
||||||
// See https://golang.org/issue/44226.
|
// maintained package, consider github.com/ProtonMail/go-crypto/openpgp, which
|
||||||
|
// is a maintained fork that aims to be a drop-in replacement for this package.
|
||||||
package s2k
|
package s2k
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -53,7 +54,7 @@ func (c *Config) hash() crypto.Hash {
|
|||||||
|
|
||||||
func (c *Config) encodedCount() uint8 {
|
func (c *Config) encodedCount() uint8 {
|
||||||
if c == nil || c.S2KCount == 0 {
|
if c == nil || c.S2KCount == 0 {
|
||||||
return 96 // The common case. Correspoding to 65536
|
return 96 // The common case. Corresponding to 65536
|
||||||
}
|
}
|
||||||
|
|
||||||
i := c.S2KCount
|
i := c.S2KCount
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
// The version of OTR implemented by this package has been deprecated
|
// The version of OTR implemented by this package has been deprecated
|
||||||
// (https://bugs.otr.im/lib/libotr/issues/140). An implementation of OTRv3 is
|
// (https://bugs.otr.im/lib/libotr/issues/140). An implementation of OTRv3 is
|
||||||
// available at https://github.com/coyim/otr3.
|
// available at https://github.com/coyim/otr3.
|
||||||
|
//
|
||||||
|
// The otr package is [frozen] and is not accepting new features.
|
||||||
|
//
|
||||||
|
// [frozen]: https://go.dev/wiki/Frozen
|
||||||
package otr
|
package otr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
+3
-3
@@ -75,8 +75,8 @@ const libOTRPrivateKey = `(privkeys
|
|||||||
(account
|
(account
|
||||||
(name "foo@example.com")
|
(name "foo@example.com")
|
||||||
(protocol prpl-jabber)
|
(protocol prpl-jabber)
|
||||||
(private-key
|
(private-key
|
||||||
(dsa
|
(dsa
|
||||||
(p #00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB8C031D3561FECEE72EBB4A090D450A9B7A857#)
|
(p #00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB8C031D3561FECEE72EBB4A090D450A9B7A857#)
|
||||||
(q #00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#)
|
(q #00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#)
|
||||||
(g #535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57597766A2F9CE3857D7ACE3E1E3BC1FC6F26#)
|
(g #535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57597766A2F9CE3857D7ACE3E1E3BC1FC6F26#)
|
||||||
@@ -402,7 +402,7 @@ func TestAgainstLibOTR(t *testing.T) {
|
|||||||
// This test requires otr.c.test to be built as /tmp/a.out.
|
// This test requires otr.c.test to be built as /tmp/a.out.
|
||||||
// If enabled, this tests runs forever performing OTR handshakes in a
|
// If enabled, this tests runs forever performing OTR handshakes in a
|
||||||
// loop.
|
// loop.
|
||||||
return
|
t.Skip("This test requires otr.c.test to be built as /tmp/a.out")
|
||||||
|
|
||||||
alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex)
|
alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex)
|
||||||
var alice Conversation
|
var alice Conversation
|
||||||
|
|||||||
+13
-60
@@ -2,24 +2,17 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
/*
|
// Package pbkdf2 implements the key derivation function PBKDF2 as defined in
|
||||||
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
|
// RFC 8018 (PKCS #5 v2.1).
|
||||||
2898 / PKCS #5 v2.0.
|
//
|
||||||
|
// This package is a wrapper for the PBKDF2 implementation in the
|
||||||
A key derivation function is useful when encrypting data based on a password
|
// [crypto/pbkdf2] package. It is [frozen] and is not accepting new features.
|
||||||
or any other not-fully-random data. It uses a pseudorandom function to derive
|
//
|
||||||
a secure encryption key based on the password.
|
// [frozen]: https://go.dev/wiki/Frozen
|
||||||
|
|
||||||
While v2.0 of the standard defines only one pseudorandom function to use,
|
|
||||||
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
|
|
||||||
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
|
|
||||||
choose, you can pass the `New` functions from the different SHA packages to
|
|
||||||
pbkdf2.Key.
|
|
||||||
*/
|
|
||||||
package pbkdf2
|
package pbkdf2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
"crypto/pbkdf2"
|
||||||
"hash"
|
"hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,51 +20,11 @@ import (
|
|||||||
// []byte of length keylen that can be used as cryptographic key. The key is
|
// []byte of length keylen that can be used as cryptographic key. The key is
|
||||||
// derived based on the method described as PBKDF2 with the HMAC variant using
|
// derived based on the method described as PBKDF2 with the HMAC variant using
|
||||||
// the supplied hash function.
|
// the supplied hash function.
|
||||||
//
|
|
||||||
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
|
|
||||||
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
|
|
||||||
// doing:
|
|
||||||
//
|
|
||||||
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
|
|
||||||
//
|
|
||||||
// Remember to get a good random salt. At least 8 bytes is recommended by the
|
|
||||||
// RFC.
|
|
||||||
//
|
|
||||||
// Using a higher iteration count will increase the cost of an exhaustive
|
|
||||||
// search but will also make derivation proportionally slower.
|
|
||||||
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||||
prf := hmac.New(h, password)
|
out, err := pbkdf2.Key(h, string(password), salt, iter, keyLen)
|
||||||
hashLen := prf.Size()
|
if err != nil {
|
||||||
numBlocks := (keyLen + hashLen - 1) / hashLen
|
// FIPS 140 enforcement, or an invalid key length.
|
||||||
|
panic(err)
|
||||||
var buf [4]byte
|
|
||||||
dk := make([]byte, 0, numBlocks*hashLen)
|
|
||||||
U := make([]byte, hashLen)
|
|
||||||
for block := 1; block <= numBlocks; block++ {
|
|
||||||
// N.B.: || means concatenation, ^ means XOR
|
|
||||||
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
|
|
||||||
// U_1 = PRF(password, salt || uint(i))
|
|
||||||
prf.Reset()
|
|
||||||
prf.Write(salt)
|
|
||||||
buf[0] = byte(block >> 24)
|
|
||||||
buf[1] = byte(block >> 16)
|
|
||||||
buf[2] = byte(block >> 8)
|
|
||||||
buf[3] = byte(block)
|
|
||||||
prf.Write(buf[:4])
|
|
||||||
dk = prf.Sum(dk)
|
|
||||||
T := dk[len(dk)-hashLen:]
|
|
||||||
copy(U, T)
|
|
||||||
|
|
||||||
// U_n = PRF(password, U_(n-1))
|
|
||||||
for n := 2; n <= iter; n++ {
|
|
||||||
prf.Reset()
|
|
||||||
prf.Write(U)
|
|
||||||
U = U[:0]
|
|
||||||
U = prf.Sum(U)
|
|
||||||
for x := range U {
|
|
||||||
T[x] ^= U[x]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return dk[:keyLen]
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher
|
|||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.Iterations < 0 || params.Iterations > maxIterations {
|
||||||
|
return nil, 0, NotImplementedError("iteration count is invalid or too high")
|
||||||
|
}
|
||||||
|
|
||||||
key := cipherType.deriveKey(params.Salt, password, params.Iterations)
|
key := cipherType.deriveKey(params.Salt, password, params.Iterations)
|
||||||
iv := cipherType.deriveIV(params.Salt, password, params.Iterations)
|
iv := cipherType.deriveIV(params.Salt, password, params.Iterations)
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,46 @@ import (
|
|||||||
|
|
||||||
var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
|
var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
|
||||||
|
|
||||||
|
func TestPbDecrypterForIterationLimit(t *testing.T) {
|
||||||
|
pass, _ := bmpString("Sesame open")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
iterations int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"at limit", maxIterations, false},
|
||||||
|
{"over limit", maxIterations + 1, true},
|
||||||
|
{"negative", -1, true},
|
||||||
|
{"max int", 1<<31 - 1, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
params, _ := asn1.Marshal(pbeParams{
|
||||||
|
Salt: []byte{1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
|
Iterations: tt.iterations,
|
||||||
|
})
|
||||||
|
alg := pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: sha1WithTripleDES,
|
||||||
|
Parameters: asn1.RawValue{
|
||||||
|
FullBytes: params,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, _, err := pbDecrypterFor(alg, pass)
|
||||||
|
if tt.wantErr {
|
||||||
|
if _, ok := err.(NotImplementedError); !ok {
|
||||||
|
t.Errorf("iterations=%d: got %v, want NotImplementedError", tt.iterations, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("iterations=%d: unexpected error: %v", tt.iterations, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPbDecrypterFor(t *testing.T) {
|
func TestPbDecrypterFor(t *testing.T) {
|
||||||
params, _ := asn1.Marshal(pbeParams{
|
params, _ := asn1.Marshal(pbeParams{
|
||||||
Salt: []byte{1, 2, 3, 4, 5, 6, 7, 8},
|
Salt: []byte{1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
|
|||||||
@@ -27,11 +27,19 @@ var (
|
|||||||
oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26})
|
oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// maxIterations is a safety limit to prevent CPU exhaustion from
|
||||||
|
// crafted PKCS#12 files with unreasonable iteration counts.
|
||||||
|
const maxIterations = 1 << 20 // ~1 million
|
||||||
|
|
||||||
func verifyMac(macData *macData, message, password []byte) error {
|
func verifyMac(macData *macData, message, password []byte) error {
|
||||||
if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) {
|
if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) {
|
||||||
return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String())
|
return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if macData.Iterations < 0 || macData.Iterations > maxIterations {
|
||||||
|
return NotImplementedError("iteration count is invalid or too high")
|
||||||
|
}
|
||||||
|
|
||||||
key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20)
|
key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20)
|
||||||
|
|
||||||
mac := hmac.New(sha1.New, key)
|
mac := hmac.New(sha1.New, key)
|
||||||
|
|||||||
@@ -5,10 +5,52 @@
|
|||||||
package pkcs12
|
package pkcs12
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestVerifyMacIterationLimit(t *testing.T) {
|
||||||
|
password, _ := bmpString("Sesame open")
|
||||||
|
message := []byte{11, 12, 13, 14, 15}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
iterations int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"at limit", maxIterations, false},
|
||||||
|
{"over limit", maxIterations + 1, true},
|
||||||
|
{"negative", -1, true},
|
||||||
|
{"max int", 1<<31 - 1, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
td := macData{
|
||||||
|
Mac: digestInfo{
|
||||||
|
Algorithm: pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: oidSHA1,
|
||||||
|
},
|
||||||
|
Digest: nil, // will fail MAC check, but iteration check comes first
|
||||||
|
},
|
||||||
|
MacSalt: []byte{1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
|
Iterations: tt.iterations,
|
||||||
|
}
|
||||||
|
err := verifyMac(&td, message, password)
|
||||||
|
if tt.wantErr {
|
||||||
|
if _, ok := err.(NotImplementedError); !ok {
|
||||||
|
t.Errorf("iterations=%d: got %v, want NotImplementedError", tt.iterations, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, ok := err.(NotImplementedError); ok {
|
||||||
|
t.Errorf("iterations=%d: got unexpected NotImplementedError", tt.iterations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVerifyMac(t *testing.T) {
|
func TestVerifyMac(t *testing.T) {
|
||||||
td := macData{
|
td := macData{
|
||||||
Mac: digestInfo{
|
Mac: digestInfo{
|
||||||
|
|||||||
+9
-5
@@ -4,12 +4,16 @@
|
|||||||
|
|
||||||
// Package pkcs12 implements some of PKCS#12.
|
// Package pkcs12 implements some of PKCS#12.
|
||||||
//
|
//
|
||||||
// This implementation is distilled from https://tools.ietf.org/html/rfc7292
|
// This implementation is distilled from [RFC 7292] and referenced documents.
|
||||||
// and referenced documents. It is intended for decoding P12/PFX-stored
|
// It is intended for decoding P12/PFX-stored certificates and keys for use
|
||||||
// certificates and keys for use with the crypto/tls package.
|
// with the crypto/tls package.
|
||||||
//
|
//
|
||||||
// This package is frozen. If it's missing functionality you need, consider
|
// The pkcs12 package is [frozen] and is not accepting new features.
|
||||||
// an alternative like software.sslmate.com/src/go-pkcs12.
|
// If it's missing functionality you need, consider an alternative like
|
||||||
|
// software.sslmate.com/src/go-pkcs12.
|
||||||
|
//
|
||||||
|
// [RFC 7292]: https://datatracker.ietf.org/doc/html/rfc7292
|
||||||
|
// [frozen]: https://go.dev/wiki/Frozen
|
||||||
package pkcs12
|
package pkcs12
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package salsa provides low-level access to functions in the Salsa family.
|
// Package salsa provides low-level access to functions in the Salsa family.
|
||||||
|
//
|
||||||
|
// Deprecated: this package exposes unsafe low-level operations. New applications
|
||||||
|
// should consider using the AEAD construction in golang.org/x/crypto/chacha20poly1305
|
||||||
|
// instead. Existing users should migrate to golang.org/x/crypto/salsa20.
|
||||||
package salsa
|
package salsa
|
||||||
|
|
||||||
import "math/bits"
|
import "math/bits"
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build loong64 && !purego && gc
|
||||||
|
|
||||||
|
package salsa
|
||||||
|
|
||||||
|
import "golang.org/x/sys/cpu"
|
||||||
|
|
||||||
|
// xorKeyStreamVX is implemented in salsa20_loong64.s.
|
||||||
|
//
|
||||||
|
//go:noescape
|
||||||
|
func xorKeyStreamVX(out, in *byte, n uint64, nonce, key *byte)
|
||||||
|
|
||||||
|
// XORKeyStream crypts bytes from in to out using the given key and counters.
|
||||||
|
// In and out must overlap entirely or not at all. Counter
|
||||||
|
// contains the raw salsa20 counter bytes (both nonce and block counter).
|
||||||
|
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = out[len(in)-1]
|
||||||
|
if cpu.Loong64.HasLSX {
|
||||||
|
xorKeyStreamVX(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0])
|
||||||
|
} else {
|
||||||
|
genericXORKeyStream(out, in, counter, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,454 @@
|
|||||||
|
// Copyright 2026 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !purego && gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
DATA ·constants+0x00(SB)/4, $0x61707865
|
||||||
|
DATA ·constants+0x04(SB)/4, $0x3320646e
|
||||||
|
DATA ·constants+0x08(SB)/4, $0x79622d32
|
||||||
|
DATA ·constants+0x0c(SB)/4, $0x6b206574
|
||||||
|
GLOBL ·constants(SB), NOPTR|RODATA, $32
|
||||||
|
|
||||||
|
#define NUM_ROUNDS 10
|
||||||
|
|
||||||
|
// func xorKeyStreamVX(out, in *byte, n uint64, counter, key *byte)
|
||||||
|
TEXT ·xorKeyStreamVX(SB), $0-40
|
||||||
|
MOVV out+0(FP), R4
|
||||||
|
MOVV in+8(FP), R5
|
||||||
|
MOVV n+16(FP), R6
|
||||||
|
MOVV counter+24(FP), R7
|
||||||
|
MOVV key+32(FP), R8
|
||||||
|
MOVV $·constants(SB), R10
|
||||||
|
|
||||||
|
BGE R0, R6, ret
|
||||||
|
|
||||||
|
MOVV 8(R7), R9 // counter[8:16]
|
||||||
|
|
||||||
|
loop256:
|
||||||
|
MOVV $NUM_ROUNDS, R15
|
||||||
|
VXORV V30, V30, V30 // V30 = 0
|
||||||
|
|
||||||
|
// load contants
|
||||||
|
VMOVQ (R10), V0.W4
|
||||||
|
VMOVQ 4(R10), V5.W4
|
||||||
|
VMOVQ 8(R10), V10.W4
|
||||||
|
VMOVQ 12(R10), V15.W4
|
||||||
|
|
||||||
|
// load keys
|
||||||
|
VMOVQ (R8), V1.W4
|
||||||
|
VMOVQ 4(R8), V2.W4
|
||||||
|
VMOVQ 8(R8), V3.W4
|
||||||
|
VMOVQ 12(R8), V4.W4
|
||||||
|
VMOVQ 16(R8), V11.W4
|
||||||
|
VMOVQ 20(R8), V12.W4
|
||||||
|
VMOVQ 24(R8), V13.W4
|
||||||
|
VMOVQ 28(R8), V14.W4
|
||||||
|
|
||||||
|
// load and update counter
|
||||||
|
VMOVQ (R7), V6.W4
|
||||||
|
VMOVQ 4(R7), V7.W4
|
||||||
|
|
||||||
|
ADDV $1, R9, R11
|
||||||
|
ADDV $2, R9, R12
|
||||||
|
ADDV $3, R9, R13
|
||||||
|
VMOVQ R9, V8.W[0]
|
||||||
|
VMOVQ R11, V8.W[1]
|
||||||
|
VMOVQ R12, V8.W[2]
|
||||||
|
VMOVQ R13, V8.W[3]
|
||||||
|
SRLV $32, R9, R14
|
||||||
|
SRLV $32, R11, R11
|
||||||
|
SRLV $32, R12, R12
|
||||||
|
SRLV $32, R13, R13
|
||||||
|
VMOVQ R14, V9.W[0]
|
||||||
|
VMOVQ R11, V9.W[1]
|
||||||
|
VMOVQ R12, V9.W[2]
|
||||||
|
VMOVQ R13, V9.W[3]
|
||||||
|
|
||||||
|
// backup V8 and V9
|
||||||
|
VADDV V8, V30, V24 // V21 = V8
|
||||||
|
VADDV V9, V30, V25 // V22 = V9
|
||||||
|
|
||||||
|
salsa20_256:
|
||||||
|
VADDW V0, V12, V26
|
||||||
|
VADDW V5, V1, V27
|
||||||
|
VADDW V10, V6, V28
|
||||||
|
VADDW V15, V11, V29
|
||||||
|
VROTRW $25, V26, V26
|
||||||
|
VROTRW $25, V27, V27
|
||||||
|
VROTRW $25, V28, V28
|
||||||
|
VROTRW $25, V29, V29
|
||||||
|
VXORV V4, V26, V4
|
||||||
|
VXORV V9, V27, V9
|
||||||
|
VXORV V14, V28, V14
|
||||||
|
VXORV V3, V29, V3
|
||||||
|
VADDW V4, V0, V26
|
||||||
|
VADDW V9, V5, V27
|
||||||
|
VADDW V14, V10, V28
|
||||||
|
VADDW V3, V15, V29
|
||||||
|
VROTRW $23, V26, V26
|
||||||
|
VROTRW $23, V27, V27
|
||||||
|
VROTRW $23, V28, V28
|
||||||
|
VROTRW $23, V29, V29
|
||||||
|
VXORV V8, V26, V8
|
||||||
|
VXORV V13, V27, V13
|
||||||
|
VXORV V2, V28, V2
|
||||||
|
VXORV V7, V29, V7
|
||||||
|
VADDW V8, V4, V26
|
||||||
|
VADDW V13, V9, V27
|
||||||
|
VADDW V2, V14, V28
|
||||||
|
VADDW V7, V3, V29
|
||||||
|
VROTRW $19, V26, V26
|
||||||
|
VROTRW $19, V27, V27
|
||||||
|
VROTRW $19, V28, V28
|
||||||
|
VROTRW $19, V29, V29
|
||||||
|
VXORV V12, V26, V12
|
||||||
|
VXORV V1, V27, V1
|
||||||
|
VXORV V6, V28, V6
|
||||||
|
VXORV V11, V29, V11
|
||||||
|
VADDW V12, V8, V26
|
||||||
|
VADDW V1, V13, V27
|
||||||
|
VADDW V6, V2, V28
|
||||||
|
VADDW V11, V7, V29
|
||||||
|
VROTRW $14, V26, V26
|
||||||
|
VROTRW $14, V27, V27
|
||||||
|
VROTRW $14, V28, V28
|
||||||
|
VROTRW $14, V29, V29
|
||||||
|
VXORV V0, V26, V0
|
||||||
|
VXORV V5, V27, V5
|
||||||
|
VXORV V10, V28, V10
|
||||||
|
VXORV V15, V29, V15
|
||||||
|
|
||||||
|
VADDW V0, V3, V26
|
||||||
|
VADDW V5, V4, V27
|
||||||
|
VADDW V10, V9, V28
|
||||||
|
VADDW V15, V14, V29
|
||||||
|
VROTRW $25, V26, V26
|
||||||
|
VROTRW $25, V27, V27
|
||||||
|
VROTRW $25, V28, V28
|
||||||
|
VROTRW $25, V29, V29
|
||||||
|
VXORV V1, V26, V1
|
||||||
|
VXORV V6, V27, V6
|
||||||
|
VXORV V11, V28, V11
|
||||||
|
VXORV V12, V29, V12
|
||||||
|
VADDW V1, V0, V26
|
||||||
|
VADDW V6, V5, V27
|
||||||
|
VADDW V11, V10, V28
|
||||||
|
VADDW V12, V15, V29
|
||||||
|
VROTRW $23, V26, V26
|
||||||
|
VROTRW $23, V27, V27
|
||||||
|
VROTRW $23, V28, V28
|
||||||
|
VROTRW $23, V29, V29
|
||||||
|
VXORV V2, V26, V2
|
||||||
|
VXORV V7, V27, V7
|
||||||
|
VXORV V8, V28, V8
|
||||||
|
VXORV V13, V29, V13
|
||||||
|
VADDW V2, V1, V26
|
||||||
|
VADDW V7, V6, V27
|
||||||
|
VADDW V8, V11, V28
|
||||||
|
VADDW V13, V12, V29
|
||||||
|
VROTRW $19, V26, V26
|
||||||
|
VROTRW $19, V27, V27
|
||||||
|
VROTRW $19, V28, V28
|
||||||
|
VROTRW $19, V29, V29
|
||||||
|
VXORV V3, V26, V3
|
||||||
|
VXORV V4, V27, V4
|
||||||
|
VXORV V9, V28, V9
|
||||||
|
VXORV V14, V29, V14
|
||||||
|
VADDW V3, V2, V26
|
||||||
|
VADDW V4, V7, V27
|
||||||
|
VADDW V9, V8, V28
|
||||||
|
VADDW V14, V13, V29
|
||||||
|
VROTRW $14, V26, V26
|
||||||
|
VROTRW $14, V27, V27
|
||||||
|
VROTRW $14, V28, V28
|
||||||
|
VROTRW $14, V29, V29
|
||||||
|
VXORV V0, V26, V0
|
||||||
|
VXORV V5, V27, V5
|
||||||
|
VXORV V10, V28, V10
|
||||||
|
VXORV V15, V29, V15
|
||||||
|
|
||||||
|
SUBV $1, R15
|
||||||
|
BNE R15, R0, salsa20_256
|
||||||
|
|
||||||
|
// load origin contants
|
||||||
|
VMOVQ (R10), V16.W4
|
||||||
|
VMOVQ 4(R10), V21.W4
|
||||||
|
VMOVQ 8(R10), V26.W4
|
||||||
|
VMOVQ 12(R10), V31.W4
|
||||||
|
|
||||||
|
// load origin keys
|
||||||
|
VMOVQ (R8), V17.W4
|
||||||
|
VMOVQ 4(R8), V18.W4
|
||||||
|
VMOVQ 8(R8), V19.W4
|
||||||
|
VMOVQ 12(R8), V20.W4
|
||||||
|
VMOVQ 16(R8), V27.W4
|
||||||
|
VMOVQ 20(R8), V28.W4
|
||||||
|
VMOVQ 24(R8), V29.W4
|
||||||
|
VMOVQ 28(R8), V30.W4
|
||||||
|
|
||||||
|
// load origin counter
|
||||||
|
VMOVQ (R7), V22.W4
|
||||||
|
VMOVQ 4(R7), V23.W4
|
||||||
|
|
||||||
|
// add back the initial state to generate the key stream
|
||||||
|
VADDW V0, V16, V0
|
||||||
|
VADDW V1, V17, V1
|
||||||
|
VADDW V2, V18, V2
|
||||||
|
VADDW V3, V19, V3
|
||||||
|
VADDW V4, V20, V4
|
||||||
|
VADDW V5, V21, V5
|
||||||
|
VADDW V6, V22, V6
|
||||||
|
VADDW V7, V23, V7
|
||||||
|
VADDW V8, V24, V8
|
||||||
|
VADDW V9, V25, V9
|
||||||
|
VADDW V10, V26, V10
|
||||||
|
VADDW V11, V27, V11
|
||||||
|
VADDW V12, V28, V12
|
||||||
|
VADDW V13, V29, V13
|
||||||
|
VADDW V14, V30, V14
|
||||||
|
VADDW V15, V31, V15
|
||||||
|
|
||||||
|
// shuffle
|
||||||
|
VILVLW V0, V1, V16
|
||||||
|
VILVHW V0, V1, V17
|
||||||
|
VILVLW V2, V3, V18
|
||||||
|
VILVHW V2, V3, V19
|
||||||
|
VILVLW V4, V5 ,V20
|
||||||
|
VILVHW V4, V5, V21
|
||||||
|
VILVLW V6, V7, V22
|
||||||
|
VILVHW V6, V7, V23
|
||||||
|
VILVLW V8, V9, V24
|
||||||
|
VILVHW V8, V9, V25
|
||||||
|
VILVLW V10, V11, V26
|
||||||
|
VILVHW V10, V11, V27
|
||||||
|
VILVLW V12, V13, V28
|
||||||
|
VILVHW V12, V13, V29
|
||||||
|
VILVLW V14, V15, V30
|
||||||
|
VILVHW V14, V15, V31
|
||||||
|
VILVLV V16, V18, V0
|
||||||
|
VILVHV V16, V18, V4
|
||||||
|
VILVLV V17, V19, V8
|
||||||
|
VILVHV V17, V19, V12
|
||||||
|
VILVLV V20, V22, V1
|
||||||
|
VILVHV V20, V22, V5
|
||||||
|
VILVLV V21, V23, V9
|
||||||
|
VILVHV V21, V23, V13
|
||||||
|
VILVLV V24, V26, V2
|
||||||
|
VILVHV V24, V26, V6
|
||||||
|
VILVLV V25, V27, V10
|
||||||
|
VILVHV V25, V27, V14
|
||||||
|
VILVLV V28, V30, V3
|
||||||
|
VILVHV V28, V30, V7
|
||||||
|
VILVLV V29, V31, V11
|
||||||
|
VILVHV V29, V31, V15
|
||||||
|
|
||||||
|
SGTU $256, R6, R11
|
||||||
|
BNE R11, R0, less_than_256
|
||||||
|
|
||||||
|
// load src data from R5
|
||||||
|
VMOVQ 0(R5), V16
|
||||||
|
VMOVQ 16(R5), V17
|
||||||
|
VMOVQ 32(R5), V18
|
||||||
|
VMOVQ 48(R5), V19
|
||||||
|
VMOVQ 64(R5), V20
|
||||||
|
VMOVQ 80(R5), V21
|
||||||
|
VMOVQ 96(R5), V22
|
||||||
|
VMOVQ 112(R5), V23
|
||||||
|
VMOVQ 128(R5), V24
|
||||||
|
VMOVQ 144(R5), V25
|
||||||
|
VMOVQ 160(R5), V26
|
||||||
|
VMOVQ 176(R5), V27
|
||||||
|
VMOVQ 192(R5), V28
|
||||||
|
VMOVQ 208(R5), V29
|
||||||
|
VMOVQ 224(R5), V30
|
||||||
|
VMOVQ 240(R5), V31
|
||||||
|
|
||||||
|
VXORV V0, V16, V16
|
||||||
|
VXORV V1, V17, V17
|
||||||
|
VXORV V2, V18, V18
|
||||||
|
VXORV V3, V19, V19
|
||||||
|
VXORV V4, V20, V20
|
||||||
|
VXORV V5, V21, V21
|
||||||
|
VXORV V6, V22, V22
|
||||||
|
VXORV V7, V23, V23
|
||||||
|
VXORV V8, V24, V24
|
||||||
|
VXORV V9, V25, V25
|
||||||
|
VXORV V10, V26, V26
|
||||||
|
VXORV V11, V27, V27
|
||||||
|
VXORV V12, V28, V28
|
||||||
|
VXORV V13, V29, V29
|
||||||
|
VXORV V14, V30, V30
|
||||||
|
VXORV V15, V31, V31
|
||||||
|
|
||||||
|
VMOVQ V16, 0(R4)
|
||||||
|
VMOVQ V17, 16(R4)
|
||||||
|
VMOVQ V18, 32(R4)
|
||||||
|
VMOVQ V19, 48(R4)
|
||||||
|
VMOVQ V20, 64(R4)
|
||||||
|
VMOVQ V21, 80(R4)
|
||||||
|
VMOVQ V22, 96(R4)
|
||||||
|
VMOVQ V23, 112(R4)
|
||||||
|
VMOVQ V24, 128(R4)
|
||||||
|
VMOVQ V25, 144(R4)
|
||||||
|
VMOVQ V26, 160(R4)
|
||||||
|
VMOVQ V27, 176(R4)
|
||||||
|
VMOVQ V28, 192(R4)
|
||||||
|
VMOVQ V29, 208(R4)
|
||||||
|
VMOVQ V30, 224(R4)
|
||||||
|
VMOVQ V31, 240(R4)
|
||||||
|
|
||||||
|
ADDV $4, R9, R9 // update counter
|
||||||
|
|
||||||
|
SUBV $256, R6, R6
|
||||||
|
ADDV $256, R4, R4
|
||||||
|
ADDV $256, R5, R5
|
||||||
|
SGTU $256, R6, R11
|
||||||
|
BEQ R11, R0, loop256
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
|
||||||
|
less_than_256:
|
||||||
|
VXORV V30, V30, V30 // V30=0
|
||||||
|
SGTU $128, R6, R11
|
||||||
|
BNE R11, R0, less_than_128
|
||||||
|
SUBV $128, R6
|
||||||
|
VMOVQ (R5), V16
|
||||||
|
VMOVQ 16(R5), V17
|
||||||
|
VMOVQ 32(R5), V18
|
||||||
|
VMOVQ 48(R5), V19
|
||||||
|
VMOVQ 64(R5), V20
|
||||||
|
VMOVQ 80(R5), V21
|
||||||
|
VMOVQ 96(R5), V22
|
||||||
|
VMOVQ 112(R5), V23
|
||||||
|
VXORV V0, V16, V16
|
||||||
|
VXORV V1, V17, V17
|
||||||
|
VXORV V2, V18, V18
|
||||||
|
VXORV V3, V19, V19
|
||||||
|
VXORV V4, V20, V20
|
||||||
|
VXORV V5, V21, V21
|
||||||
|
VXORV V6, V22, V22
|
||||||
|
VXORV V7, V23, V23
|
||||||
|
VMOVQ V16, (R4)
|
||||||
|
VMOVQ V17, 16(R4)
|
||||||
|
VMOVQ V18, 32(R4)
|
||||||
|
VMOVQ V19, 48(R4)
|
||||||
|
VMOVQ V20, 64(R4)
|
||||||
|
VMOVQ V21, 80(R4)
|
||||||
|
VMOVQ V22, 96(R4)
|
||||||
|
VMOVQ V23, 112(R4)
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
ADDV $128, R5, R5
|
||||||
|
ADDV $128, R4, R4
|
||||||
|
VADDV V8, V30, V0
|
||||||
|
VADDV V9, V30, V1
|
||||||
|
VADDV V10, V30, V2
|
||||||
|
VADDV V11, V30, V3
|
||||||
|
VADDV V12, V30, V4
|
||||||
|
VADDV V13, V30, V5
|
||||||
|
VADDV V14, V30, V6
|
||||||
|
VADDV V15, V30, V7
|
||||||
|
|
||||||
|
less_than_128:
|
||||||
|
SGTU $64, R6, R11
|
||||||
|
BNE R11, R0, less_than_64
|
||||||
|
SUBV $64, R6
|
||||||
|
VMOVQ (R5), V16
|
||||||
|
VMOVQ 16(R5), V17
|
||||||
|
VMOVQ 32(R5), V18
|
||||||
|
VMOVQ 48(R5), V19
|
||||||
|
VXORV V0, V16, V16
|
||||||
|
VXORV V1, V17, V17
|
||||||
|
VXORV V2, V18, V18
|
||||||
|
VXORV V3, V19, V19
|
||||||
|
VMOVQ V16, (R4)
|
||||||
|
VMOVQ V17, 16(R4)
|
||||||
|
VMOVQ V18, 32(R4)
|
||||||
|
VMOVQ V19, 48(R4)
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
ADDV $64, R5
|
||||||
|
ADDV $64, R4
|
||||||
|
VADDV V4, V30, V0
|
||||||
|
VADDV V5, V30, V1
|
||||||
|
VADDV V6, V30, V2
|
||||||
|
VADDV V7, V30, V3
|
||||||
|
|
||||||
|
less_than_64:
|
||||||
|
SGTU $32, R6, R11
|
||||||
|
BNE R11, R0, less_than_32
|
||||||
|
SUBV $32, R6
|
||||||
|
VMOVQ (R5), V16
|
||||||
|
VMOVQ 16(R5), V17
|
||||||
|
VXORV V0, V16, V16
|
||||||
|
VXORV V1, V17, V17
|
||||||
|
VMOVQ V16, (R4)
|
||||||
|
VMOVQ V17, 16(R4)
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
ADDV $32, R5
|
||||||
|
ADDV $32, R4
|
||||||
|
VADDV V2, V30, V0
|
||||||
|
VADDV V3, V30, V1
|
||||||
|
|
||||||
|
less_than_32:
|
||||||
|
SGTU $16, R6, R11
|
||||||
|
BNE R11, R0, less_than_16
|
||||||
|
SUBV $16, R6
|
||||||
|
VMOVQ (R5), V16
|
||||||
|
VXORV V16, V0, V16
|
||||||
|
VMOVQ V16, (R4)
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
ADDV $16, R5
|
||||||
|
ADDV $16, R4
|
||||||
|
VADDV V1, V30, V0
|
||||||
|
|
||||||
|
less_than_16:
|
||||||
|
SGTU $8, R6, R11
|
||||||
|
BNE R11, R0, less_than_8
|
||||||
|
SUBV $8, R6
|
||||||
|
VMOVQ V0.V[0], R11
|
||||||
|
VMOVQ V0.V[1], R13
|
||||||
|
MOVV (R5), R12
|
||||||
|
XOR R11, R12, R12
|
||||||
|
MOVV R12, (R4)
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
ADDV $8, R5
|
||||||
|
ADDV $8, R4
|
||||||
|
VMOVQ R13, V0.V[0]
|
||||||
|
|
||||||
|
less_than_8:
|
||||||
|
SGTU $4, R6, R11
|
||||||
|
BNE R11, R0, less_than_4
|
||||||
|
SUBV $4, R6
|
||||||
|
VMOVQ V0.W[0], R11
|
||||||
|
VMOVQ V0.W[1], R13
|
||||||
|
MOVWU (R5), R12
|
||||||
|
XOR R11, R12, R12
|
||||||
|
MOVWU R12, (R4)
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
ADDV $4, R5
|
||||||
|
ADDV $4, R4
|
||||||
|
VMOVQ R13, V0.W[0]
|
||||||
|
|
||||||
|
less_than_4:
|
||||||
|
SGTU $2, R6, R11
|
||||||
|
BNE R11, R0, less_than_2
|
||||||
|
SUBV $2, R6
|
||||||
|
VMOVQ V0.H[0], R11
|
||||||
|
VMOVQ V0.H[1], R13
|
||||||
|
MOVHU (R5), R12
|
||||||
|
XOR R11, R12, R12
|
||||||
|
MOVHU R12, (R4)
|
||||||
|
BEQ R6, R0, ret
|
||||||
|
ADDV $2, R5
|
||||||
|
ADDV $2, R4
|
||||||
|
VMOVQ R13, V0.H[0]
|
||||||
|
|
||||||
|
less_than_2:
|
||||||
|
VMOVQ V0.B[0], R11
|
||||||
|
MOVBU (R5), R12
|
||||||
|
XOR R11, R12, R12
|
||||||
|
MOVBU R12, (R4)
|
||||||
|
|
||||||
|
ret:
|
||||||
|
RET
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || purego || !gc
|
//go:build (!amd64 && !loong64) || purego || !gc
|
||||||
|
|
||||||
package salsa
|
package salsa
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && !purego && gc
|
//go:build (amd64 || loong64) && !purego && gc
|
||||||
|
|
||||||
package salsa
|
package salsa
|
||||||
|
|
||||||
@@ -196,6 +196,9 @@ func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) {
|
|||||||
if N <= 1 || N&(N-1) != 0 {
|
if N <= 1 || N&(N-1) != 0 {
|
||||||
return nil, errors.New("scrypt: N must be > 1 and a power of 2")
|
return nil, errors.New("scrypt: N must be > 1 and a power of 2")
|
||||||
}
|
}
|
||||||
|
if r <= 0 || p <= 0 {
|
||||||
|
return nil, errors.New("scrypt: parameters must be > 0")
|
||||||
|
}
|
||||||
if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r {
|
if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r {
|
||||||
return nil, errors.New("scrypt: parameters are too large")
|
return nil, errors.New("scrypt: parameters are too large")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ var bad = []testVector{
|
|||||||
{"p", "s", 1, 1, 1, nil}, // N == 1
|
{"p", "s", 1, 1, 1, nil}, // N == 1
|
||||||
{"p", "s", 7, 8, 1, nil}, // N is not power of 2
|
{"p", "s", 7, 8, 1, nil}, // N is not power of 2
|
||||||
{"p", "s", 16, maxInt / 2, maxInt / 2, nil}, // p * r too large
|
{"p", "s", 16, maxInt / 2, maxInt / 2, nil}, // p * r too large
|
||||||
|
{"p", "s", 2, 0, 1, nil}, // r too small
|
||||||
|
{"p", "s", 2, 1, 0, nil}, // p too small
|
||||||
|
{"p", "s", 2, -1, 1, nil}, // r is negative
|
||||||
|
{"p", "s", 2, 1, -1, nil}, // p is negative
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKey(t *testing.T) {
|
func TestKey(t *testing.T) {
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
module sha3/_asm
|
|
||||||
|
|
||||||
go 1.22
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/mmcloughlin/avo v0.6.0
|
|
||||||
golang.org/x/crypto v0.33.0
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
golang.org/x/mod v0.19.0 // indirect
|
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
|
||||||
golang.org/x/tools v0.23.0 // indirect
|
|
||||||
)
|
|
||||||
@@ -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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
|
||||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
|
||||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
|
||||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
|
||||||
@@ -1,438 +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.
|
|
||||||
|
|
||||||
// This code was translated into a form compatible with 6a from the public
|
|
||||||
// domain sources at https://github.com/gvanas/KeccakCodePackage
|
|
||||||
|
|
||||||
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 ../keccakf_amd64.s -pkg sha3
|
|
||||||
|
|
||||||
// Round Constants for use in the ι step.
|
|
||||||
var RoundConstants = [24]uint64{
|
|
||||||
0x0000000000000001,
|
|
||||||
0x0000000000008082,
|
|
||||||
0x800000000000808A,
|
|
||||||
0x8000000080008000,
|
|
||||||
0x000000000000808B,
|
|
||||||
0x0000000080000001,
|
|
||||||
0x8000000080008081,
|
|
||||||
0x8000000000008009,
|
|
||||||
0x000000000000008A,
|
|
||||||
0x0000000000000088,
|
|
||||||
0x0000000080008009,
|
|
||||||
0x000000008000000A,
|
|
||||||
0x000000008000808B,
|
|
||||||
0x800000000000008B,
|
|
||||||
0x8000000000008089,
|
|
||||||
0x8000000000008003,
|
|
||||||
0x8000000000008002,
|
|
||||||
0x8000000000000080,
|
|
||||||
0x000000000000800A,
|
|
||||||
0x800000008000000A,
|
|
||||||
0x8000000080008081,
|
|
||||||
0x8000000000008080,
|
|
||||||
0x0000000080000001,
|
|
||||||
0x8000000080008008,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Temporary registers
|
|
||||||
rT1 GPPhysical = RAX
|
|
||||||
|
|
||||||
// Round vars
|
|
||||||
rpState = Mem{Base: RDI}
|
|
||||||
rpStack = Mem{Base: RSP}
|
|
||||||
|
|
||||||
rDa = RBX
|
|
||||||
rDe = RCX
|
|
||||||
rDi = RDX
|
|
||||||
rDo = R8
|
|
||||||
rDu = R9
|
|
||||||
|
|
||||||
rBa = R10
|
|
||||||
rBe = R11
|
|
||||||
rBi = R12
|
|
||||||
rBo = R13
|
|
||||||
rBu = R14
|
|
||||||
|
|
||||||
rCa = RSI
|
|
||||||
rCe = RBP
|
|
||||||
rCi = rBi
|
|
||||||
rCo = rBo
|
|
||||||
rCu = R15
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
_ba = iota * 8
|
|
||||||
_be
|
|
||||||
_bi
|
|
||||||
_bo
|
|
||||||
_bu
|
|
||||||
_ga
|
|
||||||
_ge
|
|
||||||
_gi
|
|
||||||
_go
|
|
||||||
_gu
|
|
||||||
_ka
|
|
||||||
_ke
|
|
||||||
_ki
|
|
||||||
_ko
|
|
||||||
_ku
|
|
||||||
_ma
|
|
||||||
_me
|
|
||||||
_mi
|
|
||||||
_mo
|
|
||||||
_mu
|
|
||||||
_sa
|
|
||||||
_se
|
|
||||||
_si
|
|
||||||
_so
|
|
||||||
_su
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
Package("golang.org/x/crypto/sha3")
|
|
||||||
ConstraintExpr("amd64,!purego,gc")
|
|
||||||
keccakF1600()
|
|
||||||
Generate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func MOVQ_RBI_RCE() { MOVQ(rBi, rCe) }
|
|
||||||
func XORQ_RT1_RCA() { XORQ(rT1, rCa) }
|
|
||||||
func XORQ_RT1_RCE() { XORQ(rT1, rCe) }
|
|
||||||
func XORQ_RBA_RCU() { XORQ(rBa, rCu) }
|
|
||||||
func XORQ_RBE_RCU() { XORQ(rBe, rCu) }
|
|
||||||
func XORQ_RDU_RCU() { XORQ(rDu, rCu) }
|
|
||||||
func XORQ_RDA_RCA() { XORQ(rDa, rCa) }
|
|
||||||
func XORQ_RDE_RCE() { XORQ(rDe, rCe) }
|
|
||||||
|
|
||||||
type ArgMacro func()
|
|
||||||
|
|
||||||
func mKeccakRound(
|
|
||||||
iState, oState Mem,
|
|
||||||
rc U64,
|
|
||||||
B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU,
|
|
||||||
K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA,
|
|
||||||
M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA,
|
|
||||||
S_RDE_RCE ArgMacro,
|
|
||||||
) {
|
|
||||||
Comment("Prepare round")
|
|
||||||
MOVQ(rCe, rDa)
|
|
||||||
ROLQ(Imm(1), rDa)
|
|
||||||
|
|
||||||
MOVQ(iState.Offset(_bi), rCi)
|
|
||||||
XORQ(iState.Offset(_gi), rDi)
|
|
||||||
XORQ(rCu, rDa)
|
|
||||||
XORQ(iState.Offset(_ki), rCi)
|
|
||||||
XORQ(iState.Offset(_mi), rDi)
|
|
||||||
XORQ(rDi, rCi)
|
|
||||||
|
|
||||||
MOVQ(rCi, rDe)
|
|
||||||
ROLQ(Imm(1), rDe)
|
|
||||||
|
|
||||||
MOVQ(iState.Offset(_bo), rCo)
|
|
||||||
XORQ(iState.Offset(_go), rDo)
|
|
||||||
XORQ(rCa, rDe)
|
|
||||||
XORQ(iState.Offset(_ko), rCo)
|
|
||||||
XORQ(iState.Offset(_mo), rDo)
|
|
||||||
XORQ(rDo, rCo)
|
|
||||||
|
|
||||||
MOVQ(rCo, rDi)
|
|
||||||
ROLQ(Imm(1), rDi)
|
|
||||||
|
|
||||||
MOVQ(rCu, rDo)
|
|
||||||
XORQ(rCe, rDi)
|
|
||||||
ROLQ(Imm(1), rDo)
|
|
||||||
|
|
||||||
MOVQ(rCa, rDu)
|
|
||||||
XORQ(rCi, rDo)
|
|
||||||
ROLQ(Imm(1), rDu)
|
|
||||||
|
|
||||||
Comment("Result b")
|
|
||||||
MOVQ(iState.Offset(_ba), rBa)
|
|
||||||
MOVQ(iState.Offset(_ge), rBe)
|
|
||||||
XORQ(rCo, rDu)
|
|
||||||
MOVQ(iState.Offset(_ki), rBi)
|
|
||||||
MOVQ(iState.Offset(_mo), rBo)
|
|
||||||
MOVQ(iState.Offset(_su), rBu)
|
|
||||||
XORQ(rDe, rBe)
|
|
||||||
ROLQ(Imm(44), rBe)
|
|
||||||
XORQ(rDi, rBi)
|
|
||||||
XORQ(rDa, rBa)
|
|
||||||
ROLQ(Imm(43), rBi)
|
|
||||||
|
|
||||||
MOVQ(rBe, rCa)
|
|
||||||
MOVQ(rc, rT1)
|
|
||||||
ORQ(rBi, rCa)
|
|
||||||
XORQ(rBa, rT1)
|
|
||||||
XORQ(rT1, rCa)
|
|
||||||
MOVQ(rCa, oState.Offset(_ba))
|
|
||||||
|
|
||||||
XORQ(rDu, rBu)
|
|
||||||
ROLQ(Imm(14), rBu)
|
|
||||||
MOVQ(rBa, rCu)
|
|
||||||
ANDQ(rBe, rCu)
|
|
||||||
XORQ(rBu, rCu)
|
|
||||||
MOVQ(rCu, oState.Offset(_bu))
|
|
||||||
|
|
||||||
XORQ(rDo, rBo)
|
|
||||||
ROLQ(Imm(21), rBo)
|
|
||||||
MOVQ(rBo, rT1)
|
|
||||||
ANDQ(rBu, rT1)
|
|
||||||
XORQ(rBi, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_bi))
|
|
||||||
|
|
||||||
NOTQ(rBi)
|
|
||||||
ORQ(rBa, rBu)
|
|
||||||
ORQ(rBo, rBi)
|
|
||||||
XORQ(rBo, rBu)
|
|
||||||
XORQ(rBe, rBi)
|
|
||||||
MOVQ(rBu, oState.Offset(_bo))
|
|
||||||
MOVQ(rBi, oState.Offset(_be))
|
|
||||||
B_RBI_RCE()
|
|
||||||
|
|
||||||
Comment("Result g")
|
|
||||||
MOVQ(iState.Offset(_gu), rBe)
|
|
||||||
XORQ(rDu, rBe)
|
|
||||||
MOVQ(iState.Offset(_ka), rBi)
|
|
||||||
ROLQ(Imm(20), rBe)
|
|
||||||
XORQ(rDa, rBi)
|
|
||||||
ROLQ(Imm(3), rBi)
|
|
||||||
MOVQ(iState.Offset(_bo), rBa)
|
|
||||||
MOVQ(rBe, rT1)
|
|
||||||
ORQ(rBi, rT1)
|
|
||||||
XORQ(rDo, rBa)
|
|
||||||
MOVQ(iState.Offset(_me), rBo)
|
|
||||||
MOVQ(iState.Offset(_si), rBu)
|
|
||||||
ROLQ(Imm(28), rBa)
|
|
||||||
XORQ(rBa, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_ga))
|
|
||||||
G_RT1_RCA()
|
|
||||||
|
|
||||||
XORQ(rDe, rBo)
|
|
||||||
ROLQ(Imm(45), rBo)
|
|
||||||
MOVQ(rBi, rT1)
|
|
||||||
ANDQ(rBo, rT1)
|
|
||||||
XORQ(rBe, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_ge))
|
|
||||||
G_RT1_RCE()
|
|
||||||
|
|
||||||
XORQ(rDi, rBu)
|
|
||||||
ROLQ(Imm(61), rBu)
|
|
||||||
MOVQ(rBu, rT1)
|
|
||||||
ORQ(rBa, rT1)
|
|
||||||
XORQ(rBo, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_go))
|
|
||||||
|
|
||||||
ANDQ(rBe, rBa)
|
|
||||||
XORQ(rBu, rBa)
|
|
||||||
MOVQ(rBa, oState.Offset(_gu))
|
|
||||||
NOTQ(rBu)
|
|
||||||
G_RBA_RCU()
|
|
||||||
|
|
||||||
ORQ(rBu, rBo)
|
|
||||||
XORQ(rBi, rBo)
|
|
||||||
MOVQ(rBo, oState.Offset(_gi))
|
|
||||||
|
|
||||||
Comment("Result k")
|
|
||||||
MOVQ(iState.Offset(_be), rBa)
|
|
||||||
MOVQ(iState.Offset(_gi), rBe)
|
|
||||||
MOVQ(iState.Offset(_ko), rBi)
|
|
||||||
MOVQ(iState.Offset(_mu), rBo)
|
|
||||||
MOVQ(iState.Offset(_sa), rBu)
|
|
||||||
XORQ(rDi, rBe)
|
|
||||||
ROLQ(Imm(6), rBe)
|
|
||||||
XORQ(rDo, rBi)
|
|
||||||
ROLQ(Imm(25), rBi)
|
|
||||||
MOVQ(rBe, rT1)
|
|
||||||
ORQ(rBi, rT1)
|
|
||||||
XORQ(rDe, rBa)
|
|
||||||
ROLQ(Imm(1), rBa)
|
|
||||||
XORQ(rBa, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_ka))
|
|
||||||
K_RT1_RCA()
|
|
||||||
|
|
||||||
XORQ(rDu, rBo)
|
|
||||||
ROLQ(Imm(8), rBo)
|
|
||||||
MOVQ(rBi, rT1)
|
|
||||||
ANDQ(rBo, rT1)
|
|
||||||
XORQ(rBe, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_ke))
|
|
||||||
K_RT1_RCE()
|
|
||||||
|
|
||||||
XORQ(rDa, rBu)
|
|
||||||
ROLQ(Imm(18), rBu)
|
|
||||||
NOTQ(rBo)
|
|
||||||
MOVQ(rBo, rT1)
|
|
||||||
ANDQ(rBu, rT1)
|
|
||||||
XORQ(rBi, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_ki))
|
|
||||||
|
|
||||||
MOVQ(rBu, rT1)
|
|
||||||
ORQ(rBa, rT1)
|
|
||||||
XORQ(rBo, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_ko))
|
|
||||||
|
|
||||||
ANDQ(rBe, rBa)
|
|
||||||
XORQ(rBu, rBa)
|
|
||||||
MOVQ(rBa, oState.Offset(_ku))
|
|
||||||
K_RBA_RCU()
|
|
||||||
|
|
||||||
Comment("Result m")
|
|
||||||
MOVQ(iState.Offset(_ga), rBe)
|
|
||||||
XORQ(rDa, rBe)
|
|
||||||
MOVQ(iState.Offset(_ke), rBi)
|
|
||||||
ROLQ(Imm(36), rBe)
|
|
||||||
XORQ(rDe, rBi)
|
|
||||||
MOVQ(iState.Offset(_bu), rBa)
|
|
||||||
ROLQ(Imm(10), rBi)
|
|
||||||
MOVQ(rBe, rT1)
|
|
||||||
MOVQ(iState.Offset(_mi), rBo)
|
|
||||||
ANDQ(rBi, rT1)
|
|
||||||
XORQ(rDu, rBa)
|
|
||||||
MOVQ(iState.Offset(_so), rBu)
|
|
||||||
ROLQ(Imm(27), rBa)
|
|
||||||
XORQ(rBa, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_ma))
|
|
||||||
M_RT1_RCA()
|
|
||||||
|
|
||||||
XORQ(rDi, rBo)
|
|
||||||
ROLQ(Imm(15), rBo)
|
|
||||||
MOVQ(rBi, rT1)
|
|
||||||
ORQ(rBo, rT1)
|
|
||||||
XORQ(rBe, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_me))
|
|
||||||
M_RT1_RCE()
|
|
||||||
|
|
||||||
XORQ(rDo, rBu)
|
|
||||||
ROLQ(Imm(56), rBu)
|
|
||||||
NOTQ(rBo)
|
|
||||||
MOVQ(rBo, rT1)
|
|
||||||
ORQ(rBu, rT1)
|
|
||||||
XORQ(rBi, rT1)
|
|
||||||
MOVQ(rT1, oState.Offset(_mi))
|
|
||||||
|
|
||||||
ORQ(rBa, rBe)
|
|
||||||
XORQ(rBu, rBe)
|
|
||||||
MOVQ(rBe, oState.Offset(_mu))
|
|
||||||
|
|
||||||
ANDQ(rBa, rBu)
|
|
||||||
XORQ(rBo, rBu)
|
|
||||||
MOVQ(rBu, oState.Offset(_mo))
|
|
||||||
M_RBE_RCU()
|
|
||||||
|
|
||||||
Comment("Result s")
|
|
||||||
MOVQ(iState.Offset(_bi), rBa)
|
|
||||||
MOVQ(iState.Offset(_go), rBe)
|
|
||||||
MOVQ(iState.Offset(_ku), rBi)
|
|
||||||
XORQ(rDi, rBa)
|
|
||||||
MOVQ(iState.Offset(_ma), rBo)
|
|
||||||
ROLQ(Imm(62), rBa)
|
|
||||||
XORQ(rDo, rBe)
|
|
||||||
MOVQ(iState.Offset(_se), rBu)
|
|
||||||
ROLQ(Imm(55), rBe)
|
|
||||||
|
|
||||||
XORQ(rDu, rBi)
|
|
||||||
MOVQ(rBa, rDu)
|
|
||||||
XORQ(rDe, rBu)
|
|
||||||
ROLQ(Imm(2), rBu)
|
|
||||||
ANDQ(rBe, rDu)
|
|
||||||
XORQ(rBu, rDu)
|
|
||||||
MOVQ(rDu, oState.Offset(_su))
|
|
||||||
|
|
||||||
ROLQ(Imm(39), rBi)
|
|
||||||
S_RDU_RCU()
|
|
||||||
NOTQ(rBe)
|
|
||||||
XORQ(rDa, rBo)
|
|
||||||
MOVQ(rBe, rDa)
|
|
||||||
ANDQ(rBi, rDa)
|
|
||||||
XORQ(rBa, rDa)
|
|
||||||
MOVQ(rDa, oState.Offset(_sa))
|
|
||||||
S_RDA_RCA()
|
|
||||||
|
|
||||||
ROLQ(Imm(41), rBo)
|
|
||||||
MOVQ(rBi, rDe)
|
|
||||||
ORQ(rBo, rDe)
|
|
||||||
XORQ(rBe, rDe)
|
|
||||||
MOVQ(rDe, oState.Offset(_se))
|
|
||||||
S_RDE_RCE()
|
|
||||||
|
|
||||||
MOVQ(rBo, rDi)
|
|
||||||
MOVQ(rBu, rDo)
|
|
||||||
ANDQ(rBu, rDi)
|
|
||||||
ORQ(rBa, rDo)
|
|
||||||
XORQ(rBi, rDi)
|
|
||||||
XORQ(rBo, rDo)
|
|
||||||
MOVQ(rDi, oState.Offset(_si))
|
|
||||||
MOVQ(rDo, oState.Offset(_so))
|
|
||||||
}
|
|
||||||
|
|
||||||
// keccakF1600 applies the Keccak permutation to a 1600b-wide
|
|
||||||
// state represented as a slice of 25 uint64s.
|
|
||||||
func keccakF1600() {
|
|
||||||
Implement("keccakF1600")
|
|
||||||
AllocLocal(200)
|
|
||||||
|
|
||||||
Load(Param("a"), rpState.Base)
|
|
||||||
|
|
||||||
Comment("Convert the user state into an internal state")
|
|
||||||
NOTQ(rpState.Offset(_be))
|
|
||||||
NOTQ(rpState.Offset(_bi))
|
|
||||||
NOTQ(rpState.Offset(_go))
|
|
||||||
NOTQ(rpState.Offset(_ki))
|
|
||||||
NOTQ(rpState.Offset(_mi))
|
|
||||||
NOTQ(rpState.Offset(_sa))
|
|
||||||
|
|
||||||
Comment("Execute the KeccakF permutation")
|
|
||||||
MOVQ(rpState.Offset(_ba), rCa)
|
|
||||||
MOVQ(rpState.Offset(_be), rCe)
|
|
||||||
MOVQ(rpState.Offset(_bu), rCu)
|
|
||||||
|
|
||||||
XORQ(rpState.Offset(_ga), rCa)
|
|
||||||
XORQ(rpState.Offset(_ge), rCe)
|
|
||||||
XORQ(rpState.Offset(_gu), rCu)
|
|
||||||
|
|
||||||
XORQ(rpState.Offset(_ka), rCa)
|
|
||||||
XORQ(rpState.Offset(_ke), rCe)
|
|
||||||
XORQ(rpState.Offset(_ku), rCu)
|
|
||||||
|
|
||||||
XORQ(rpState.Offset(_ma), rCa)
|
|
||||||
XORQ(rpState.Offset(_me), rCe)
|
|
||||||
XORQ(rpState.Offset(_mu), rCu)
|
|
||||||
|
|
||||||
XORQ(rpState.Offset(_sa), rCa)
|
|
||||||
XORQ(rpState.Offset(_se), rCe)
|
|
||||||
MOVQ(rpState.Offset(_si), rDi)
|
|
||||||
MOVQ(rpState.Offset(_so), rDo)
|
|
||||||
XORQ(rpState.Offset(_su), rCu)
|
|
||||||
|
|
||||||
for i, rc := range RoundConstants[:len(RoundConstants)-1] {
|
|
||||||
var iState, oState Mem
|
|
||||||
if i%2 == 0 {
|
|
||||||
iState, oState = rpState, rpStack
|
|
||||||
} else {
|
|
||||||
iState, oState = rpStack, rpState
|
|
||||||
}
|
|
||||||
mKeccakRound(iState, oState, U64(rc), MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
|
||||||
}
|
|
||||||
mKeccakRound(rpStack, rpState, U64(RoundConstants[len(RoundConstants)-1]), NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP)
|
|
||||||
|
|
||||||
Comment("Revert the internal state to the user state")
|
|
||||||
NOTQ(rpState.Offset(_be))
|
|
||||||
NOTQ(rpState.Offset(_bi))
|
|
||||||
NOTQ(rpState.Offset(_go))
|
|
||||||
NOTQ(rpState.Offset(_ki))
|
|
||||||
NOTQ(rpState.Offset(_mi))
|
|
||||||
NOTQ(rpState.Offset(_sa))
|
|
||||||
|
|
||||||
RET()
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
// Copyright 2023 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 !noopt
|
|
||||||
|
|
||||||
package sha3_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
)
|
|
||||||
|
|
||||||
var sink byte
|
|
||||||
|
|
||||||
func TestAllocations(t *testing.T) {
|
|
||||||
want := 0.0
|
|
||||||
|
|
||||||
if runtime.GOARCH == "s390x" {
|
|
||||||
// On s390x the returned hash.Hash is conditional so it escapes.
|
|
||||||
want = 3.0
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("New", func(t *testing.T) {
|
|
||||||
if allocs := testing.AllocsPerRun(10, func() {
|
|
||||||
h := sha3.New256()
|
|
||||||
b := []byte("ABC")
|
|
||||||
h.Write(b)
|
|
||||||
out := make([]byte, 0, 32)
|
|
||||||
out = h.Sum(out)
|
|
||||||
sink ^= out[0]
|
|
||||||
}); allocs > want {
|
|
||||||
t.Errorf("expected zero allocations, got %0.1f", allocs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("NewShake", func(t *testing.T) {
|
|
||||||
if allocs := testing.AllocsPerRun(10, func() {
|
|
||||||
h := sha3.NewShake128()
|
|
||||||
b := []byte("ABC")
|
|
||||||
h.Write(b)
|
|
||||||
out := make([]byte, 0, 32)
|
|
||||||
out = h.Sum(out)
|
|
||||||
sink ^= out[0]
|
|
||||||
h.Read(out)
|
|
||||||
sink ^= out[0]
|
|
||||||
}); allocs > want {
|
|
||||||
t.Errorf("expected zero allocations, got %0.1f", allocs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("Sum", func(t *testing.T) {
|
|
||||||
if allocs := testing.AllocsPerRun(10, func() {
|
|
||||||
b := []byte("ABC")
|
|
||||||
out := sha3.Sum256(b)
|
|
||||||
sink ^= out[0]
|
|
||||||
}); allocs > want {
|
|
||||||
t.Errorf("expected zero allocations, got %0.1f", allocs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
-66
@@ -1,66 +0,0 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package sha3 implements the SHA-3 fixed-output-length hash functions and
|
|
||||||
// the SHAKE variable-output-length hash functions defined by FIPS-202.
|
|
||||||
//
|
|
||||||
// All types in this package also implement [encoding.BinaryMarshaler],
|
|
||||||
// [encoding.BinaryAppender] and [encoding.BinaryUnmarshaler] to marshal and
|
|
||||||
// unmarshal the internal state of the hash.
|
|
||||||
//
|
|
||||||
// Both types of hash function use the "sponge" construction and the Keccak
|
|
||||||
// permutation. For a detailed specification see http://keccak.noekeon.org/
|
|
||||||
//
|
|
||||||
// # Guidance
|
|
||||||
//
|
|
||||||
// If you aren't sure what function you need, use SHAKE256 with at least 64
|
|
||||||
// bytes of output. The SHAKE instances are faster than the SHA3 instances;
|
|
||||||
// the latter have to allocate memory to conform to the hash.Hash interface.
|
|
||||||
//
|
|
||||||
// If you need a secret-key MAC (message authentication code), prepend the
|
|
||||||
// secret key to the input, hash with SHAKE256 and read at least 32 bytes of
|
|
||||||
// output.
|
|
||||||
//
|
|
||||||
// # Security strengths
|
|
||||||
//
|
|
||||||
// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security
|
|
||||||
// strength against preimage attacks of x bits. Since they only produce "x"
|
|
||||||
// bits of output, their collision-resistance is only "x/2" bits.
|
|
||||||
//
|
|
||||||
// The SHAKE-256 and -128 functions have a generic security strength of 256 and
|
|
||||||
// 128 bits against all attacks, provided that at least 2x bits of their output
|
|
||||||
// is used. Requesting more than 64 or 32 bytes of output, respectively, does
|
|
||||||
// not increase the collision-resistance of the SHAKE functions.
|
|
||||||
//
|
|
||||||
// # The sponge construction
|
|
||||||
//
|
|
||||||
// A sponge builds a pseudo-random function from a public pseudo-random
|
|
||||||
// permutation, by applying the permutation to a state of "rate + capacity"
|
|
||||||
// bytes, but hiding "capacity" of the bytes.
|
|
||||||
//
|
|
||||||
// A sponge starts out with a zero state. To hash an input using a sponge, up
|
|
||||||
// to "rate" bytes of the input are XORed into the sponge's state. The sponge
|
|
||||||
// is then "full" and the permutation is applied to "empty" it. This process is
|
|
||||||
// repeated until all the input has been "absorbed". The input is then padded.
|
|
||||||
// The digest is "squeezed" from the sponge in the same way, except that output
|
|
||||||
// is copied out instead of input being XORed in.
|
|
||||||
//
|
|
||||||
// A sponge is parameterized by its generic security strength, which is equal
|
|
||||||
// to half its capacity; capacity + rate is equal to the permutation's width.
|
|
||||||
// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means
|
|
||||||
// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2.
|
|
||||||
//
|
|
||||||
// # Recommendations
|
|
||||||
//
|
|
||||||
// The SHAKE functions are recommended for most new uses. They can produce
|
|
||||||
// output of arbitrary length. SHAKE256, with an output length of at least
|
|
||||||
// 64 bytes, provides 256-bit security against all attacks. The Keccak team
|
|
||||||
// recommends it for most applications upgrading from SHA2-512. (NIST chose a
|
|
||||||
// much stronger, but much slower, sponge instance for SHA3-512.)
|
|
||||||
//
|
|
||||||
// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions.
|
|
||||||
// They produce output of the same length, with the same security strengths
|
|
||||||
// against all attacks. This means, in particular, that SHA3-256 only has
|
|
||||||
// 128-bit collision resistance, because its output length is 32 bytes.
|
|
||||||
package sha3
|
|
||||||
+50
-83
@@ -2,127 +2,94 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package sha3 implements the SHA-3 hash algorithms and the SHAKE extendable
|
||||||
|
// output functions defined in FIPS 202.
|
||||||
|
//
|
||||||
|
// Most of this package is a wrapper around the crypto/sha3 package in the
|
||||||
|
// standard library. The only exception is the legacy Keccak hash functions.
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
// This file provides functions for creating instances of the SHA-3
|
|
||||||
// and SHAKE hash functions, as well as utility functions for hashing
|
|
||||||
// bytes.
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto/sha3"
|
||||||
"hash"
|
"hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New224 creates a new SHA3-224 hash.
|
// New224 creates a new SHA3-224 hash.
|
||||||
// Its generic security strength is 224 bits against preimage attacks,
|
// Its generic security strength is 224 bits against preimage attacks,
|
||||||
// and 112 bits against collision attacks.
|
// and 112 bits against collision attacks.
|
||||||
|
//
|
||||||
|
// It is a wrapper for the [sha3.New224] function in the standard library.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func New224() hash.Hash {
|
func New224() hash.Hash {
|
||||||
return new224()
|
return sha3.New224()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New256 creates a new SHA3-256 hash.
|
// New256 creates a new SHA3-256 hash.
|
||||||
// Its generic security strength is 256 bits against preimage attacks,
|
// Its generic security strength is 256 bits against preimage attacks,
|
||||||
// and 128 bits against collision attacks.
|
// and 128 bits against collision attacks.
|
||||||
|
//
|
||||||
|
// It is a wrapper for the [sha3.New256] function in the standard library.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func New256() hash.Hash {
|
func New256() hash.Hash {
|
||||||
return new256()
|
return sha3.New256()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New384 creates a new SHA3-384 hash.
|
// New384 creates a new SHA3-384 hash.
|
||||||
// Its generic security strength is 384 bits against preimage attacks,
|
// Its generic security strength is 384 bits against preimage attacks,
|
||||||
// and 192 bits against collision attacks.
|
// and 192 bits against collision attacks.
|
||||||
|
//
|
||||||
|
// It is a wrapper for the [sha3.New384] function in the standard library.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func New384() hash.Hash {
|
func New384() hash.Hash {
|
||||||
return new384()
|
return sha3.New384()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New512 creates a new SHA3-512 hash.
|
// New512 creates a new SHA3-512 hash.
|
||||||
// Its generic security strength is 512 bits against preimage attacks,
|
// Its generic security strength is 512 bits against preimage attacks,
|
||||||
// and 256 bits against collision attacks.
|
// and 256 bits against collision attacks.
|
||||||
|
//
|
||||||
|
// It is a wrapper for the [sha3.New512] function in the standard library.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func New512() hash.Hash {
|
func New512() hash.Hash {
|
||||||
return new512()
|
return sha3.New512()
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
crypto.RegisterHash(crypto.SHA3_224, New224)
|
|
||||||
crypto.RegisterHash(crypto.SHA3_256, New256)
|
|
||||||
crypto.RegisterHash(crypto.SHA3_384, New384)
|
|
||||||
crypto.RegisterHash(crypto.SHA3_512, New512)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
dsbyteSHA3 = 0b00000110
|
|
||||||
dsbyteKeccak = 0b00000001
|
|
||||||
dsbyteShake = 0b00011111
|
|
||||||
dsbyteCShake = 0b00000100
|
|
||||||
|
|
||||||
// rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in
|
|
||||||
// bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits.
|
|
||||||
rateK256 = (1600 - 256) / 8
|
|
||||||
rateK448 = (1600 - 448) / 8
|
|
||||||
rateK512 = (1600 - 512) / 8
|
|
||||||
rateK768 = (1600 - 768) / 8
|
|
||||||
rateK1024 = (1600 - 1024) / 8
|
|
||||||
)
|
|
||||||
|
|
||||||
func new224Generic() *state {
|
|
||||||
return &state{rate: rateK448, outputLen: 28, dsbyte: dsbyteSHA3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func new256Generic() *state {
|
|
||||||
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteSHA3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func new384Generic() *state {
|
|
||||||
return &state{rate: rateK768, outputLen: 48, dsbyte: dsbyteSHA3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func new512Generic() *state {
|
|
||||||
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteSHA3}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLegacyKeccak256 creates a new Keccak-256 hash.
|
|
||||||
//
|
|
||||||
// Only use this function if you require compatibility with an existing cryptosystem
|
|
||||||
// that uses non-standard padding. All other users should use New256 instead.
|
|
||||||
func NewLegacyKeccak256() hash.Hash {
|
|
||||||
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLegacyKeccak512 creates a new Keccak-512 hash.
|
|
||||||
//
|
|
||||||
// Only use this function if you require compatibility with an existing cryptosystem
|
|
||||||
// that uses non-standard padding. All other users should use New512 instead.
|
|
||||||
func NewLegacyKeccak512() hash.Hash {
|
|
||||||
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum224 returns the SHA3-224 digest of the data.
|
// Sum224 returns the SHA3-224 digest of the data.
|
||||||
func Sum224(data []byte) (digest [28]byte) {
|
//
|
||||||
h := New224()
|
// It is a wrapper for the [sha3.Sum224] function in the standard library.
|
||||||
h.Write(data)
|
//
|
||||||
h.Sum(digest[:0])
|
//go:fix inline
|
||||||
return
|
func Sum224(data []byte) [28]byte {
|
||||||
|
return sha3.Sum224(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum256 returns the SHA3-256 digest of the data.
|
// Sum256 returns the SHA3-256 digest of the data.
|
||||||
func Sum256(data []byte) (digest [32]byte) {
|
//
|
||||||
h := New256()
|
// It is a wrapper for the [sha3.Sum256] function in the standard library.
|
||||||
h.Write(data)
|
//
|
||||||
h.Sum(digest[:0])
|
//go:fix inline
|
||||||
return
|
func Sum256(data []byte) [32]byte {
|
||||||
|
return sha3.Sum256(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum384 returns the SHA3-384 digest of the data.
|
// Sum384 returns the SHA3-384 digest of the data.
|
||||||
func Sum384(data []byte) (digest [48]byte) {
|
//
|
||||||
h := New384()
|
// It is a wrapper for the [sha3.Sum384] function in the standard library.
|
||||||
h.Write(data)
|
//
|
||||||
h.Sum(digest[:0])
|
//go:fix inline
|
||||||
return
|
func Sum384(data []byte) [48]byte {
|
||||||
|
return sha3.Sum384(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sum512 returns the SHA3-512 digest of the data.
|
// Sum512 returns the SHA3-512 digest of the data.
|
||||||
func Sum512(data []byte) (digest [64]byte) {
|
//
|
||||||
h := New512()
|
// It is a wrapper for the [sha3.Sum512] function in the standard library.
|
||||||
h.Write(data)
|
//
|
||||||
h.Sum(digest[:0])
|
//go:fix inline
|
||||||
return
|
func Sum512(data []byte) [64]byte {
|
||||||
|
return sha3.Sum512(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
// Copyright 2023 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !gc || purego || !s390x
|
|
||||||
|
|
||||||
package sha3
|
|
||||||
|
|
||||||
func new224() *state {
|
|
||||||
return new224Generic()
|
|
||||||
}
|
|
||||||
|
|
||||||
func new256() *state {
|
|
||||||
return new256Generic()
|
|
||||||
}
|
|
||||||
|
|
||||||
func new384() *state {
|
|
||||||
return new384Generic()
|
|
||||||
}
|
|
||||||
|
|
||||||
func new512() *state {
|
|
||||||
return new512Generic()
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build amd64 && !purego && gc
|
|
||||||
|
|
||||||
package sha3
|
|
||||||
|
|
||||||
// This function is implemented in keccakf_amd64.s.
|
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
|
|
||||||
func keccakF1600(a *[25]uint64)
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user