1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build (amd64 || arm64 || ppc64 || ppc64le) && !purego
6
7package aes
8
9import (
10	"crypto/cipher"
11	"crypto/internal/alias"
12	"crypto/internal/boring"
13	"internal/cpu"
14	"internal/goarch"
15)
16
17// defined in asm_*.s
18
19//go:noescape
20func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
21
22//go:noescape
23func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
24
25//go:noescape
26func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
27
28type aesCipherAsm struct {
29	aesCipher
30}
31
32// aesCipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
33// will use the optimised implementation in aes_gcm.go when possible.
34// Instances of this type only exist when hasGCMAsm returns true. Likewise,
35// the gcmAble implementation is in aes_gcm.go.
36type aesCipherGCM struct {
37	aesCipherAsm
38}
39
40var supportsAES = cpu.X86.HasAES || cpu.ARM64.HasAES || goarch.IsPpc64 == 1 || goarch.IsPpc64le == 1
41var supportsGFMUL = cpu.X86.HasPCLMULQDQ || cpu.ARM64.HasPMULL
42
43func newCipher(key []byte) (cipher.Block, error) {
44	if !supportsAES {
45		return newCipherGeneric(key)
46	}
47	// Note that under certain circumstances, we only return the inner aesCipherAsm.
48	// This avoids an unnecessary allocation of the aesCipher struct.
49	c := aesCipherGCM{aesCipherAsm{aesCipher{l: uint8(len(key) + 28)}}}
50	var rounds int
51	switch len(key) {
52	case 128 / 8:
53		rounds = 10
54	case 192 / 8:
55		rounds = 12
56	case 256 / 8:
57		rounds = 14
58	default:
59		return nil, KeySizeError(len(key))
60	}
61
62	expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0])
63	if supportsAES && supportsGFMUL {
64		return &c, nil
65	}
66	return &c.aesCipherAsm, nil
67}
68
69func (c *aesCipherAsm) BlockSize() int { return BlockSize }
70
71func (c *aesCipherAsm) Encrypt(dst, src []byte) {
72	boring.Unreachable()
73	if len(src) < BlockSize {
74		panic("crypto/aes: input not full block")
75	}
76	if len(dst) < BlockSize {
77		panic("crypto/aes: output not full block")
78	}
79	if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
80		panic("crypto/aes: invalid buffer overlap")
81	}
82	encryptBlockAsm(int(c.l)/4-1, &c.enc[0], &dst[0], &src[0])
83}
84
85func (c *aesCipherAsm) Decrypt(dst, src []byte) {
86	boring.Unreachable()
87	if len(src) < BlockSize {
88		panic("crypto/aes: input not full block")
89	}
90	if len(dst) < BlockSize {
91		panic("crypto/aes: output not full block")
92	}
93	if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
94		panic("crypto/aes: invalid buffer overlap")
95	}
96	decryptBlockAsm(int(c.l)/4-1, &c.dec[0], &dst[0], &src[0])
97}
98
99// expandKey is used by BenchmarkExpand to ensure that the asm implementation
100// of key expansion is used for the benchmark when it is available.
101func expandKey(key []byte, enc, dec []uint32) {
102	if supportsAES {
103		rounds := 10 // rounds needed for AES128
104		switch len(key) {
105		case 192 / 8:
106			rounds = 12
107		case 256 / 8:
108			rounds = 14
109		}
110		expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
111	} else {
112		expandKeyGo(key, enc, dec)
113	}
114}
115