Add ODoH support. (#1653)
This commit is contained in:
parent
f7219b2bfa
commit
c748f93752
|
@ -146,3 +146,25 @@ t || dig -p${DNS_PORT} A MICROSOFT.COM @127.0.0.1 | grep -Fq "NOERROR" || fail
|
||||||
|
|
||||||
kill $(cat /tmp/dnscrypt-proxy.pidfile)
|
kill $(cat /tmp/dnscrypt-proxy.pidfile)
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
|
section
|
||||||
|
../dnscrypt-proxy/dnscrypt-proxy -loglevel 3 -config test-odoh-direct.toml -pidfile /tmp/odoh-direct.pidfile &
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
section
|
||||||
|
t || dig -p${DNS_PORT} A microsoft.com @127.0.0.1 | grep -Fq "NOERROR" || fail
|
||||||
|
t || dig -p${DNS_PORT} A cloudflare.com @127.0.0.1 | grep -Fq "NOERROR" || fail
|
||||||
|
|
||||||
|
kill $(cat /tmp/odoh-direct.pidfile)
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
section
|
||||||
|
../dnscrypt-proxy/dnscrypt-proxy -loglevel 3 -config test-odoh-proxied.toml -pidfile /tmp/odoh-proxied.pidfile &
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
section
|
||||||
|
t || dig -p${DNS_PORT} A microsoft.com @127.0.0.1 | grep -Fq "NOERROR" || fail
|
||||||
|
t || dig -p${DNS_PORT} A cloudflare.com @127.0.0.1 | grep -Fq "NOERROR" || fail
|
||||||
|
|
||||||
|
kill $(cat /tmp/odoh-proxied.pidfile)
|
||||||
|
sleep 5
|
||||||
|
|
|
@ -39,6 +39,7 @@ jobs:
|
||||||
- name: Test suite
|
- name: Test suite
|
||||||
run: |
|
run: |
|
||||||
go version
|
go version
|
||||||
|
go mod vendor
|
||||||
cd .ci
|
cd .ci
|
||||||
./ci-test.sh
|
./ci-test.sh
|
||||||
cd -
|
cd -
|
||||||
|
|
|
@ -718,8 +718,8 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
|
||||||
hasSpecificRoutes := false
|
hasSpecificRoutes := false
|
||||||
for _, server := range proxy.registeredServers {
|
for _, server := range proxy.registeredServers {
|
||||||
if via, ok := (*proxy.routes)[server.name]; ok {
|
if via, ok := (*proxy.routes)[server.name]; ok {
|
||||||
if server.stamp.Proto != stamps.StampProtoTypeDNSCrypt {
|
if server.stamp.Proto != stamps.StampProtoTypeDNSCrypt && server.stamp.Proto != stamps.StampProtoTypeODoHTarget {
|
||||||
dlog.Errorf("DNS anonymization is only supported with the DNSCrypt protocol - Connections to [%v] cannot be anonymized", server.name)
|
dlog.Errorf("DNS anonymization is only supported with the DNSCrypt and ODoH protocols - Connections to [%v] cannot be anonymized", server.name)
|
||||||
} else {
|
} else {
|
||||||
dlog.Noticef("Anonymized DNS: routing [%v] via %v", server.name, via)
|
dlog.Noticef("Anonymized DNS: routing [%v] via %v", server.name, via)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
hpkecompact "github.com/jedisct1/go-hpke-compact"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
odohVersion = uint16(0xff06)
|
||||||
|
)
|
||||||
|
|
||||||
|
type ODoHTarget struct {
|
||||||
|
suite *hpkecompact.Suite
|
||||||
|
keyID []byte
|
||||||
|
publicKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeLengthValue(b []byte) []byte {
|
||||||
|
lengthBuffer := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(lengthBuffer, uint16(len(b)))
|
||||||
|
return append(lengthBuffer, b...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseODoHTargetConfig(config []byte) (ODoHTarget, error) {
|
||||||
|
if len(config) < 8 {
|
||||||
|
return ODoHTarget{}, fmt.Errorf("Malformed config")
|
||||||
|
}
|
||||||
|
kemID := binary.BigEndian.Uint16(config[0:2])
|
||||||
|
kdfID := binary.BigEndian.Uint16(config[2:4])
|
||||||
|
aeadID := binary.BigEndian.Uint16(config[4:6])
|
||||||
|
publicKeyLength := binary.BigEndian.Uint16(config[6:8])
|
||||||
|
if len(config[8:]) != int(publicKeyLength) {
|
||||||
|
return ODoHTarget{}, fmt.Errorf("Malformed config")
|
||||||
|
}
|
||||||
|
|
||||||
|
suite, err := hpkecompact.NewSuite(hpkecompact.KemID(kemID), hpkecompact.KdfID(kdfID), hpkecompact.AeadID(aeadID))
|
||||||
|
if err != nil {
|
||||||
|
return ODoHTarget{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey := config[8:]
|
||||||
|
_, _, err = suite.NewClientContext(publicKey, []byte("odoh query"), nil)
|
||||||
|
if err != nil {
|
||||||
|
return ODoHTarget{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyID, err := suite.Expand(suite.Extract(config, nil), []byte("odoh key id"), uint16(suite.Hash().Size()))
|
||||||
|
if err != nil {
|
||||||
|
return ODoHTarget{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ODoHTarget{
|
||||||
|
suite: suite,
|
||||||
|
publicKey: publicKey,
|
||||||
|
keyID: encodeLengthValue(keyID),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseODoHTargetConfigs(configs []byte) ([]ODoHTarget, error) {
|
||||||
|
length := binary.BigEndian.Uint16(configs)
|
||||||
|
if len(configs) != int(length)+2 {
|
||||||
|
return nil, fmt.Errorf("Malformed configs")
|
||||||
|
}
|
||||||
|
|
||||||
|
targets := make([]ODoHTarget, 0)
|
||||||
|
offset := 2
|
||||||
|
for {
|
||||||
|
if offset+4 > len(configs) {
|
||||||
|
return targets, nil
|
||||||
|
}
|
||||||
|
configVersion := binary.BigEndian.Uint16(configs[offset : offset+2])
|
||||||
|
configLength := binary.BigEndian.Uint16(configs[offset+2 : offset+4])
|
||||||
|
if configVersion == odohVersion {
|
||||||
|
target, err := parseODoHTargetConfig(configs[offset+4 : offset+4+int(configLength)])
|
||||||
|
if err == nil {
|
||||||
|
targets = append(targets, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = offset + int(configLength) + 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ODoHQuery struct {
|
||||||
|
suite *hpkecompact.Suite
|
||||||
|
ctx hpkecompact.ClientContext
|
||||||
|
odohPlaintext []byte
|
||||||
|
odohMessage []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t ODoHTarget) encryptQuery(query []byte) (ODoHQuery, error) {
|
||||||
|
clientCtx, encryptedSharedSecret, err := t.suite.NewClientContext(t.publicKey, []byte("odoh query"), nil)
|
||||||
|
if err != nil {
|
||||||
|
return ODoHQuery{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
odohPlaintext := make([]byte, 4+len(query))
|
||||||
|
binary.BigEndian.PutUint16(odohPlaintext[0:2], uint16(len(query)))
|
||||||
|
copy(odohPlaintext[2:], query)
|
||||||
|
|
||||||
|
aad := append([]byte{0x01}, t.keyID...)
|
||||||
|
ciphertext, err := clientCtx.EncryptToServer(odohPlaintext, aad)
|
||||||
|
|
||||||
|
encryptedMessage := encodeLengthValue(append(encryptedSharedSecret, ciphertext...))
|
||||||
|
odohMessage := append(append([]byte{0x01}, t.keyID...), encryptedMessage...)
|
||||||
|
|
||||||
|
return ODoHQuery{
|
||||||
|
suite: t.suite,
|
||||||
|
odohPlaintext: odohPlaintext,
|
||||||
|
odohMessage: odohMessage,
|
||||||
|
ctx: clientCtx,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q ODoHQuery) decryptResponse(response []byte) ([]byte, error) {
|
||||||
|
if len(response) < 3 {
|
||||||
|
return nil, fmt.Errorf("Malformed response")
|
||||||
|
}
|
||||||
|
|
||||||
|
messageType := response[0]
|
||||||
|
if messageType != uint8(0x02) {
|
||||||
|
return nil, fmt.Errorf("Malformed response")
|
||||||
|
}
|
||||||
|
|
||||||
|
responseNonceLength := binary.BigEndian.Uint16(response[1:3])
|
||||||
|
if len(response) < 5+int(responseNonceLength) {
|
||||||
|
return nil, fmt.Errorf("Malformed response")
|
||||||
|
}
|
||||||
|
|
||||||
|
responseNonceEnc := response[1 : 3+responseNonceLength]
|
||||||
|
|
||||||
|
secret, err := q.ctx.Export([]byte("odoh response"), q.suite.KeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
salt := append(q.odohPlaintext, responseNonceEnc...)
|
||||||
|
prk := q.suite.Extract(secret, salt)
|
||||||
|
key, err := q.suite.Expand(prk, []byte("odoh key"), q.suite.KeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nonce, err := q.suite.Expand(prk, []byte("odoh nonce"), q.suite.NonceBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
aesgcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctLength := binary.BigEndian.Uint16(response[3+int(responseNonceLength) : 5+int(responseNonceLength)])
|
||||||
|
if int(ctLength) != len(response[5+int(responseNonceLength):]) {
|
||||||
|
return nil, fmt.Errorf("Malformed response")
|
||||||
|
}
|
||||||
|
|
||||||
|
ct := response[5+int(responseNonceLength):]
|
||||||
|
aad := response[0 : 3+int(responseNonceLength)]
|
||||||
|
|
||||||
|
responsePlaintext, err := aesgcm.Open(nil, nonce, ct, aad)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
responseLength := binary.BigEndian.Uint16(responsePlaintext[0:2])
|
||||||
|
valid := 1
|
||||||
|
for i := 4 + int(responseLength); i < len(responsePlaintext); i++ {
|
||||||
|
valid = valid & subtle.ConstantTimeByteEq(response[i], 0x00)
|
||||||
|
}
|
||||||
|
if valid != 1 {
|
||||||
|
return nil, fmt.Errorf("Malformed response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return responsePlaintext[2 : 2+int(responseLength)], nil
|
||||||
|
}
|
|
@ -658,7 +658,7 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
|
||||||
tid := TransactionID(query)
|
tid := TransactionID(query)
|
||||||
SetTransactionID(query, 0)
|
SetTransactionID(query, 0)
|
||||||
serverInfo.noticeBegin(proxy)
|
serverInfo.noticeBegin(proxy)
|
||||||
serverResponse, tls, _, err := proxy.xTransport.DoHQuery(serverInfo.useGet, serverInfo.URL, query, proxy.timeout)
|
serverResponse, _, tls, _, err := proxy.xTransport.DoHQuery(serverInfo.useGet, serverInfo.URL, query, proxy.timeout)
|
||||||
SetTransactionID(query, tid)
|
SetTransactionID(query, tid)
|
||||||
if err == nil || tls == nil || !tls.HandshakeComplete {
|
if err == nil || tls == nil || !tls.HandshakeComplete {
|
||||||
response = nil
|
response = nil
|
||||||
|
@ -678,6 +678,42 @@ func (proxy *Proxy) processIncomingQuery(clientProto string, serverProto string,
|
||||||
if len(response) >= MinDNSPacketSize {
|
if len(response) >= MinDNSPacketSize {
|
||||||
SetTransactionID(response, tid)
|
SetTransactionID(response, tid)
|
||||||
}
|
}
|
||||||
|
} else if serverInfo.Proto == stamps.StampProtoTypeODoHTarget {
|
||||||
|
tid := TransactionID(query)
|
||||||
|
target := serverInfo.odohTargets[0]
|
||||||
|
odohQuery, err := target.encryptQuery(query)
|
||||||
|
if err != nil {
|
||||||
|
dlog.Error("Failed to encrypt query")
|
||||||
|
response = nil
|
||||||
|
} else {
|
||||||
|
targetURL := serverInfo.URL
|
||||||
|
if serverInfo.Relay != nil && serverInfo.Relay.ODoH != nil {
|
||||||
|
targetURL = serverInfo.Relay.ODoH.url
|
||||||
|
}
|
||||||
|
responseBody, responseCode, _, _, err := proxy.xTransport.ObliviousDoHQuery(targetURL, odohQuery.odohMessage, proxy.timeout)
|
||||||
|
if err == nil && len(responseBody) > 0 && responseCode == 200 {
|
||||||
|
response, err = odohQuery.decryptResponse(responseBody)
|
||||||
|
if err != nil {
|
||||||
|
dlog.Error("Failed to decrypt response")
|
||||||
|
response = nil
|
||||||
|
}
|
||||||
|
} else if responseCode == 401 {
|
||||||
|
dlog.Notice("Forcing key update")
|
||||||
|
go proxy.serversInfo.refresh(proxy)
|
||||||
|
response = nil
|
||||||
|
} else {
|
||||||
|
dlog.Error("Failed to receive successful response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response) >= MinDNSPacketSize {
|
||||||
|
SetTransactionID(response, tid)
|
||||||
|
} else if response == nil {
|
||||||
|
pluginsState.returnCode = PluginsReturnCodeNetworkError
|
||||||
|
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
|
||||||
|
serverInfo.noticeFailure(proxy)
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dlog.Fatal("Unsupported protocol")
|
dlog.Fatal("Unsupported protocol")
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -61,6 +63,7 @@ type ServerInfo struct {
|
||||||
knownBugs ServerBugs
|
knownBugs ServerBugs
|
||||||
Proto stamps.StampProtoType
|
Proto stamps.StampProtoType
|
||||||
useGet bool
|
useGet bool
|
||||||
|
odohTargets []ODoHTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
type LBStrategy interface {
|
type LBStrategy interface {
|
||||||
|
@ -104,7 +107,9 @@ type DNSCryptRelay struct {
|
||||||
RelayTCPAddr *net.TCPAddr
|
RelayTCPAddr *net.TCPAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
type ODoHRelay struct{}
|
type ODoHRelay struct {
|
||||||
|
url *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
type Relay struct {
|
type Relay struct {
|
||||||
Proto stamps.StampProtoType
|
Proto stamps.StampProtoType
|
||||||
|
@ -277,6 +282,8 @@ func fetchServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew
|
||||||
return fetchDNSCryptServerInfo(proxy, name, stamp, isNew)
|
return fetchDNSCryptServerInfo(proxy, name, stamp, isNew)
|
||||||
} else if stamp.Proto == stamps.StampProtoTypeDoH {
|
} else if stamp.Proto == stamps.StampProtoTypeDoH {
|
||||||
return fetchDoHServerInfo(proxy, name, stamp, isNew)
|
return fetchDoHServerInfo(proxy, name, stamp, isNew)
|
||||||
|
} else if stamp.Proto == stamps.StampProtoTypeODoHTarget {
|
||||||
|
return fetchODoHTargetInfo(proxy, name, stamp, isNew)
|
||||||
}
|
}
|
||||||
return ServerInfo{}, fmt.Errorf("Unsupported protocol for [%s]: [%s]", name, stamp.Proto.String())
|
return ServerInfo{}, fmt.Errorf("Unsupported protocol for [%s]: [%s]", name, stamp.Proto.String())
|
||||||
}
|
}
|
||||||
|
@ -412,7 +419,26 @@ func route(proxy *Proxy, name string) (*Relay, error) {
|
||||||
dlog.Noticef("Anonymizing queries for [%v] via [%v]", name, relayName)
|
dlog.Noticef("Anonymizing queries for [%v] via [%v]", name, relayName)
|
||||||
return &Relay{Proto: stamps.StampProtoTypeDNSCryptRelay, Dnscrypt: &DNSCryptRelay{RelayUDPAddr: relayUDPAddr, RelayTCPAddr: relayTCPAddr}}, nil
|
return &Relay{Proto: stamps.StampProtoTypeDNSCryptRelay, Dnscrypt: &DNSCryptRelay{RelayUDPAddr: relayUDPAddr, RelayTCPAddr: relayTCPAddr}}, nil
|
||||||
case stamps.StampProtoTypeODoHRelay:
|
case stamps.StampProtoTypeODoHRelay:
|
||||||
return &Relay{Proto: stamps.StampProtoTypeODoHRelay, ODoH: &ODoHRelay{}}, nil
|
target, err := url.Parse("https://" + relayCandidateStamp.ProviderName + "/" + relayCandidateStamp.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, server := range proxy.registeredServers {
|
||||||
|
if server.name == name && server.stamp.Proto == stamps.StampProtoTypeODoHTarget {
|
||||||
|
qs := target.Query()
|
||||||
|
qs.Add("targethost", server.stamp.ProviderName)
|
||||||
|
qs.Add("targetpath", server.stamp.Path)
|
||||||
|
target2 := *target
|
||||||
|
target2.RawQuery = qs.Encode()
|
||||||
|
target = &target2
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Relay{Proto: stamps.StampProtoTypeODoHRelay, ODoH: &ODoHRelay{
|
||||||
|
url: target,
|
||||||
|
}}, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Invalid relay set for server [%v]", name)
|
return nil, fmt.Errorf("Invalid relay set for server [%v]", name)
|
||||||
}
|
}
|
||||||
|
@ -550,15 +576,15 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
|
||||||
proxy.xTransport.rebuildTransport()
|
proxy.xTransport.rebuildTransport()
|
||||||
}
|
}
|
||||||
useGet := false
|
useGet := false
|
||||||
if _, _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
|
if _, _, _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
|
||||||
useGet = true
|
useGet = true
|
||||||
if _, _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
|
if _, _, _, _, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout); err != nil {
|
||||||
return ServerInfo{}, err
|
return ServerInfo{}, err
|
||||||
}
|
}
|
||||||
dlog.Debugf("Server [%s] doesn't appear to support POST; falling back to GET requests", name)
|
dlog.Debugf("Server [%s] doesn't appear to support POST; falling back to GET requests", name)
|
||||||
}
|
}
|
||||||
body = dohNXTestPacket(0xcafe)
|
body = dohNXTestPacket(0xcafe)
|
||||||
serverResponse, tls, rtt, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout)
|
serverResponse, _, tls, rtt, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dlog.Infof("[%s] [%s]: %v", name, url, err)
|
dlog.Infof("[%s] [%s]: %v", name, url, err)
|
||||||
return ServerInfo{}, err
|
return ServerInfo{}, err
|
||||||
|
@ -632,6 +658,62 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fetchTargetConfigsFromWellKnown(url string) ([]ODoHTarget, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseODoHTargetConfigs(bodyBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchODoHTargetInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) {
|
||||||
|
odohTargets, err := fetchTargetConfigsFromWellKnown("https://" + stamp.ProviderName + "/.well-known/odohconfigs")
|
||||||
|
if err != nil || len(odohTargets) == 0 {
|
||||||
|
return ServerInfo{}, fmt.Errorf("[%s] does not have an Oblivious DoH configuration", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
relay, err := route(proxy, name)
|
||||||
|
if err != nil {
|
||||||
|
return ServerInfo{}, err
|
||||||
|
}
|
||||||
|
if relay == nil || relay.ODoH == nil {
|
||||||
|
relay = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if relay == nil {
|
||||||
|
dlog.Notice("Relay is empty for " + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := &url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: stamp.ProviderName,
|
||||||
|
Path: stamp.Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerInfo{
|
||||||
|
Proto: stamps.StampProtoTypeODoHTarget,
|
||||||
|
Name: name,
|
||||||
|
Timeout: proxy.timeout,
|
||||||
|
URL: url,
|
||||||
|
HostName: stamp.ProviderName,
|
||||||
|
useGet: false,
|
||||||
|
odohTargets: odohTargets,
|
||||||
|
Relay: relay,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (serverInfo *ServerInfo) noticeFailure(proxy *Proxy) {
|
func (serverInfo *ServerInfo) noticeFailure(proxy *Proxy) {
|
||||||
proxy.serversInfo.Lock()
|
proxy.serversInfo.Lock()
|
||||||
serverInfo.rtt.Add(float64(proxy.timeout.Nanoseconds() / 1000000))
|
serverInfo.rtt.Add(float64(proxy.timeout.Nanoseconds() / 1000000))
|
||||||
|
|
|
@ -132,7 +132,7 @@ func (source *Source) parseURLs(urls []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchFromURL(xTransport *XTransport, u *url.URL) (bin []byte, err error) {
|
func fetchFromURL(xTransport *XTransport, u *url.URL) (bin []byte, err error) {
|
||||||
bin, _, _, err = xTransport.Get(u, "", DefaultTimeout)
|
bin, _, _, _, err = xTransport.Get(u, "", DefaultTimeout)
|
||||||
return bin, err
|
return bin, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -338,7 +338,7 @@ func (xTransport *XTransport) resolveAndUpdateCache(host string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration) ([]byte, *tls.ConnectionState, time.Duration, error) {
|
func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = xTransport.timeout
|
timeout = xTransport.timeout
|
||||||
}
|
}
|
||||||
|
@ -361,11 +361,11 @@ func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string,
|
||||||
}
|
}
|
||||||
host, _ := ExtractHostAndPort(url.Host, 0)
|
host, _ := ExtractHostAndPort(url.Host, 0)
|
||||||
if xTransport.proxyDialer == nil && strings.HasSuffix(host, ".onion") {
|
if xTransport.proxyDialer == nil && strings.HasSuffix(host, ".onion") {
|
||||||
return nil, nil, 0, errors.New("Onion service is not reachable without Tor")
|
return nil, 0, nil, 0, errors.New("Onion service is not reachable without Tor")
|
||||||
}
|
}
|
||||||
if err := xTransport.resolveAndUpdateCache(host); err != nil {
|
if err := xTransport.resolveAndUpdateCache(host); err != nil {
|
||||||
dlog.Errorf("Unable to resolve [%v] - Make sure that the system resolver works, or that `bootstrap_resolvers` has been set to resolvers that can be reached", host)
|
dlog.Errorf("Unable to resolve [%v] - Make sure that the system resolver works, or that `bootstrap_resolvers` has been set to resolvers that can be reached", host)
|
||||||
return nil, nil, 0, err
|
return nil, 0, nil, 0, err
|
||||||
}
|
}
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: method,
|
Method: method,
|
||||||
|
@ -396,26 +396,26 @@ func (xTransport *XTransport) Fetch(method string, url *url.URL, accept string,
|
||||||
xTransport.tlsCipherSuite = nil
|
xTransport.tlsCipherSuite = nil
|
||||||
xTransport.rebuildTransport()
|
xTransport.rebuildTransport()
|
||||||
}
|
}
|
||||||
return nil, nil, 0, err
|
return nil, 0, nil, 0, err
|
||||||
}
|
}
|
||||||
tls := resp.TLS
|
tls := resp.TLS
|
||||||
bin, err := ioutil.ReadAll(io.LimitReader(resp.Body, MaxHTTPBodyLength))
|
bin, err := ioutil.ReadAll(io.LimitReader(resp.Body, MaxHTTPBodyLength))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, tls, 0, err
|
return nil, resp.StatusCode, tls, 0, err
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
return bin, tls, rtt, err
|
return bin, resp.StatusCode, tls, rtt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xTransport *XTransport) Get(url *url.URL, accept string, timeout time.Duration) ([]byte, *tls.ConnectionState, time.Duration, error) {
|
func (xTransport *XTransport) Get(url *url.URL, accept string, timeout time.Duration) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
|
||||||
return xTransport.Fetch("GET", url, accept, "", nil, timeout)
|
return xTransport.Fetch("GET", url, accept, "", nil, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xTransport *XTransport) Post(url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration) ([]byte, *tls.ConnectionState, time.Duration, error) {
|
func (xTransport *XTransport) Post(url *url.URL, accept string, contentType string, body *[]byte, timeout time.Duration) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
|
||||||
return xTransport.Fetch("POST", url, accept, contentType, body, timeout)
|
return xTransport.Fetch("POST", url, accept, contentType, body, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xTransport *XTransport) DoHQuery(useGet bool, url *url.URL, body []byte, timeout time.Duration) ([]byte, *tls.ConnectionState, time.Duration, error) {
|
func (xTransport *XTransport) DoHQuery(useGet bool, url *url.URL, body []byte, timeout time.Duration) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
|
||||||
dataType := "application/dns-message"
|
dataType := "application/dns-message"
|
||||||
if useGet {
|
if useGet {
|
||||||
qs := url.Query()
|
qs := url.Query()
|
||||||
|
@ -427,3 +427,8 @@ func (xTransport *XTransport) DoHQuery(useGet bool, url *url.URL, body []byte, t
|
||||||
}
|
}
|
||||||
return xTransport.Post(url, dataType, dataType, &body, timeout)
|
return xTransport.Post(url, dataType, dataType, &body, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (xTransport *XTransport) ObliviousDoHQuery(url *url.URL, body []byte, timeout time.Duration) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
|
||||||
|
dataType := "application/oblivious-dns-message"
|
||||||
|
return xTransport.Post(url, dataType, dataType, &body, timeout)
|
||||||
|
}
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -5,6 +5,8 @@ go 1.16
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/VividCortex/ewma v1.1.1
|
github.com/VividCortex/ewma v1.1.1
|
||||||
|
github.com/cisco/go-hpke v0.0.0-20210215210317-01c430f1f302 // indirect
|
||||||
|
github.com/cloudflare/odoh-go v0.1.6 // indirect
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185
|
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.0
|
github.com/hashicorp/go-immutable-radix v1.3.0
|
||||||
|
@ -13,6 +15,7 @@ require (
|
||||||
github.com/jedisct1/dlog v0.0.0-20210101122416-354ffe815216
|
github.com/jedisct1/dlog v0.0.0-20210101122416-354ffe815216
|
||||||
github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868
|
github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868
|
||||||
github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5
|
github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5
|
||||||
|
github.com/jedisct1/go-hpke-compact v0.0.0-20210329192501-7ceabaabca65
|
||||||
github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7
|
github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7
|
||||||
github.com/jedisct1/xsecretbox v0.0.0-20210102102453-4ecb2081017a
|
github.com/jedisct1/xsecretbox v0.0.0-20210102102453-4ecb2081017a
|
||||||
github.com/k-sone/critbitgo v1.4.0
|
github.com/k-sone/critbitgo v1.4.0
|
||||||
|
|
24
go.sum
24
go.sum
|
@ -1,4 +1,6 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
git.schwanenlied.me/yawning/x448.git v0.0.0-20170617130356-01b048fb03d6 h1:w8IZgCntCe0RuBJp+dENSMwEBl/k8saTgJ5hPca5IWw=
|
||||||
|
git.schwanenlied.me/yawning/x448.git v0.0.0-20170617130356-01b048fb03d6/go.mod h1:wQaGCqEu44ykB17jZHCevrgSVl3KJnwQBObUtrKU4uU=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||||
|
@ -8,9 +10,18 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSi
|
||||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cisco/go-hpke v0.0.0-20210215210317-01c430f1f302 h1:unAbn7dpE8eeUfWRaOPl1qTfffhIcCNuKQuECGNGWtk=
|
||||||
|
github.com/cisco/go-hpke v0.0.0-20210215210317-01c430f1f302/go.mod h1:RSsoIHRMBe69FbF/fIbmWYa3rrC6vuPyC0MbNUpel3Q=
|
||||||
|
github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b h1:Ves2turKTX7zruivAcUOQg155xggcbv3suVdbKCBQNM=
|
||||||
|
github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b/go.mod h1:0AZAV7lYvynZQ5ErHlGMKH+4QYMyNCFd+AiL9MlrCYA=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cloudflare/circl v1.0.0 h1:64b6pyfCFbYm623ncIkYGNZaOcmIbyd+CjyMi2L9vdI=
|
||||||
|
github.com/cloudflare/circl v1.0.0/go.mod h1:MhjB3NEEhJbTOdLLq964NIUisXDxaE1WkQPUxtgZXiY=
|
||||||
|
github.com/cloudflare/odoh-go v0.1.6 h1:siTTv/pBXztJORDgVNDAYKdTboenwWFMDD1B9YQHkag=
|
||||||
|
github.com/cloudflare/odoh-go v0.1.6/go.mod h1:J3Doz827YDYvz4hEmJU6q45hRFOqxUBL6NRUuEfjMxA=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
|
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
|
||||||
|
@ -54,6 +65,10 @@ github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868 h1:QZ79mRbN
|
||||||
github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw=
|
github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw=
|
||||||
github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5 h1:FnAupK0Gm6PJZDhI5sGkbNZQ7DT4+tG8opjmoXfBu/o=
|
github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5 h1:FnAupK0Gm6PJZDhI5sGkbNZQ7DT4+tG8opjmoXfBu/o=
|
||||||
github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5/go.mod h1:t35n6rsPE3nD3RXbc5hI5Ax1ci/SSYTpx0BdMXh/1aE=
|
github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5/go.mod h1:t35n6rsPE3nD3RXbc5hI5Ax1ci/SSYTpx0BdMXh/1aE=
|
||||||
|
github.com/jedisct1/go-hpke-compact v0.0.0-20210216141532-42fe67ec23b2 h1:5pGP8stsRZ3SHicLH189p/RroaZ43+m8P4+CBu3maRE=
|
||||||
|
github.com/jedisct1/go-hpke-compact v0.0.0-20210216141532-42fe67ec23b2/go.mod h1:Sc4df3OZCQgMxgbCVYLnIA16SRDKrGcSFcUqeu67MGs=
|
||||||
|
github.com/jedisct1/go-hpke-compact v0.0.0-20210329192501-7ceabaabca65 h1:qxey1Jfre+udaWyQI+lS3qPGuJDzmkBaHDIhmL9qef8=
|
||||||
|
github.com/jedisct1/go-hpke-compact v0.0.0-20210329192501-7ceabaabca65/go.mod h1:fFxJHJ4XTptLoMkie7bC9zjyKSKC8yycljJtKXGaAAI=
|
||||||
github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7 h1:qrPDNqqT76vs8oWL6Z1/D6hKvbXULvlD7FdNVTIUI8A=
|
github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7 h1:qrPDNqqT76vs8oWL6Z1/D6hKvbXULvlD7FdNVTIUI8A=
|
||||||
github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7/go.mod h1:oPTyITpvr7hPx/9w76gWrgbZwbb+7gZ9/On8hFc+LNE=
|
github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7/go.mod h1:oPTyITpvr7hPx/9w76gWrgbZwbb+7gZ9/On8hFc+LNE=
|
||||||
github.com/jedisct1/xsecretbox v0.0.0-20210102102453-4ecb2081017a h1:ptZ+a2DzulAgKEfMuqH2Ckfv6oio5BX14fRG5aShvXw=
|
github.com/jedisct1/xsecretbox v0.0.0-20210102102453-4ecb2081017a h1:ptZ+a2DzulAgKEfMuqH2Ckfv6oio5BX14fRG5aShvXw=
|
||||||
|
@ -70,6 +85,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/powerman/check v1.3.0/go.mod h1:k/8NCUQwepaKJKctBBKjQo84jvGEvKiumD9pDl87RB0=
|
||||||
github.com/powerman/check v1.3.1 h1:86xiEjMNn0K4b3bhQsLfrLqJb0DWLXHBK3wBd5PoJH4=
|
github.com/powerman/check v1.3.1 h1:86xiEjMNn0K4b3bhQsLfrLqJb0DWLXHBK3wBd5PoJH4=
|
||||||
github.com/powerman/check v1.3.1/go.mod h1:k/8NCUQwepaKJKctBBKjQo84jvGEvKiumD9pDl87RB0=
|
github.com/powerman/check v1.3.1/go.mod h1:k/8NCUQwepaKJKctBBKjQo84jvGEvKiumD9pDl87RB0=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
@ -78,8 +94,12 @@ github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHei
|
||||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -102,12 +122,15 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc=
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc=
|
||||||
|
@ -149,5 +172,6 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXL
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
*~
|
||||||
|
go.sum
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* ISC License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2021
|
||||||
|
* Frank Denis <j at pureftpd dot org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
|
@ -0,0 +1,129 @@
|
||||||
|
[![CI status](https://github.com/jedisct1/go-hpke-compact/workflows/Go/badge.svg)](https://github.com/jedisct1/go-hpke-compact/actions)
|
||||||
|
[![Go Reference](https://pkg.go.dev/badge/github.com/jedisct1/go-hpke-compact.svg)](https://pkg.go.dev/github.com/jedisct1/go-hpke-compact)
|
||||||
|
|
||||||
|
# ![HPKE-Compact](.assets/logo.png)
|
||||||
|
|
||||||
|
# A compact HPKE implemention for Go
|
||||||
|
|
||||||
|
`hpkecompact` is a small implementation of the [Hybrid Public Key Encryption](https://cfrg.github.io/draft-irtf-cfrg-hpke/draft-irtf-cfrg-hpke.html) (HPKE) draft.
|
||||||
|
|
||||||
|
It fits in a single file and only uses the Go standard library and `x/crypto`.
|
||||||
|
|
||||||
|
Suites are currently limited to `X25519-HKDF-SHA256` / `HKDF-SHA-256` / `{AES-{128,256}-GCM, CHACHA20-POLY1305}`; these are very likely to be the most commonly deployed ones for a forseable future.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Suite instantiation
|
||||||
|
|
||||||
|
```go
|
||||||
|
suite, err := NewSuite(KemX25519HkdfSha256, KdfHkdfSha256, AeadAes128Gcm)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key pair creation
|
||||||
|
|
||||||
|
```go
|
||||||
|
serverKp, err := ctx.GenerateKeyPair()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client: creation and encapsulation of the shared secret
|
||||||
|
|
||||||
|
A _client_ initiates a connexion by sending an encrypted secret; a _server_ accepts an encrypted secret from a client, and decrypts it, so that both parties can eventually agree on a shared secret.
|
||||||
|
|
||||||
|
```go
|
||||||
|
clientCtx, encryptedSharedSecret, err :=
|
||||||
|
suite.NewClientContext(serverKp.PublicKey, []byte("application name"), nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
* `encryptedSharedSecret` needs to be sent to the server.
|
||||||
|
* `clientCtx` can be used to encrypt/decrypt messages exchanged with the server.
|
||||||
|
* The last parameter is an optional pre-shared key (`Psk` type).
|
||||||
|
|
||||||
|
To improve misuse resistance, this implementation uses distinct types for the client and the server context: `ClientContext` for the client, and `ServerContext` for the server.
|
||||||
|
|
||||||
|
### Server: decapsulation of the shared secret
|
||||||
|
|
||||||
|
```go
|
||||||
|
serverCtx, err := suite.NewServerContext(encryptedSharedSecret,
|
||||||
|
serverKp, []byte("application name"), nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
* `serverCtx` can be used to encrypt/decrypt messages exchanged with the client
|
||||||
|
* The last parameter is an optional pre-shared key (`Psk` type).
|
||||||
|
|
||||||
|
### Encryption of a message from the client to the server
|
||||||
|
|
||||||
|
A message can be encrypted by the client for the server:
|
||||||
|
|
||||||
|
```go
|
||||||
|
ciphertext, err := clientCtx.EncryptToServer([]byte("message"), nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
Nonces are automatically incremented, so it is safe to call this function multiple times within the same context.
|
||||||
|
|
||||||
|
Second parameter is optional associated data.
|
||||||
|
|
||||||
|
### Decryption of a ciphertext received by the server
|
||||||
|
|
||||||
|
The server can decrypt a ciphertext sent by the client:
|
||||||
|
|
||||||
|
```go
|
||||||
|
decrypted, err := serverCtx.DecryptFromClient(ciphertext, nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
Second parameter is optional associated data.
|
||||||
|
|
||||||
|
### Encryption of a message from the server to the client
|
||||||
|
|
||||||
|
A message can also be encrypted by the server for the client:
|
||||||
|
|
||||||
|
```go
|
||||||
|
ciphertext, err := clientCtx.EncryptToClient([]byte("response"), nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
Nonces are automatically incremented, so it is safe to call this function multiple times within the same context.
|
||||||
|
|
||||||
|
Second parameter is optional associated data.
|
||||||
|
|
||||||
|
### Decryption of a ciphertext received by the client
|
||||||
|
|
||||||
|
The client can decrypt a ciphertext sent by the server:
|
||||||
|
|
||||||
|
```go
|
||||||
|
decrypted, err := serverCtx.DecryptFromServer(ciphertext, nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
Second parameter is optional associated data.
|
||||||
|
|
||||||
|
## Authenticated modes
|
||||||
|
|
||||||
|
Authenticated modes, with or without a PSK are supported.
|
||||||
|
|
||||||
|
Just replace `NewClientContext()` with `NewAuthenticatedClientContext()` and `NewServerContext()` with `NewAuthenticatedServerContext()` for authentication.
|
||||||
|
|
||||||
|
```go
|
||||||
|
clientKp, err := suite.GenerateKeyPair()
|
||||||
|
serverKp, err := suite.GenerateKeyPair()
|
||||||
|
|
||||||
|
clientCtx, encryptedSharedSecret, err := suite.NewAuthenticatedClientContext(
|
||||||
|
clientKp, serverKp.PublicKey, []byte("app"), psk)
|
||||||
|
|
||||||
|
serverCtx, err := suite.NewAuthenticatedServerContext(
|
||||||
|
clientKp.PublicKey, encryptedSharedSecret, serverKp, []byte("app"), psk)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exporter secret
|
||||||
|
|
||||||
|
The exporter secret can be obtained with the `ExportedSecret()` function available both in the `ServerContext` and `ClientContext` structures:
|
||||||
|
|
||||||
|
```go
|
||||||
|
exporter := serverCtx.ExporterSecret()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key derivation
|
||||||
|
|
||||||
|
```go
|
||||||
|
secret1, err := clientCtx.Export("description 1")
|
||||||
|
secret2, err := serverCtx.Export("description 2");
|
||||||
|
```
|
||||||
|
|
||||||
|
## That's it!
|
|
@ -0,0 +1,8 @@
|
||||||
|
module github.com/jedisct1/go-hpke-compact
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/powerman/check v1.3.1
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
|
)
|
|
@ -0,0 +1,656 @@
|
||||||
|
package hpkecompact
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
crypto_rand "crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
"golang.org/x/crypto/curve25519"
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hpkeVersion = [7]byte{'H', 'P', 'K', 'E', '-', 'v', '1'}
|
||||||
|
|
||||||
|
// Mode - Mode
|
||||||
|
type Mode byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ModeBase - Base mode
|
||||||
|
ModeBase Mode = 0x00
|
||||||
|
// ModePsk - PSK mode
|
||||||
|
ModePsk Mode = 0x01
|
||||||
|
// ModeAuth - Auth mode
|
||||||
|
ModeAuth Mode = 0x02
|
||||||
|
// ModeAuthPsk - PSK Auth mode
|
||||||
|
ModeAuthPsk Mode = 0x03
|
||||||
|
)
|
||||||
|
|
||||||
|
// KemID - KEM ID
|
||||||
|
type KemID uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
// KemX25519HkdfSha256 - X25519 with HKDF-SHA256
|
||||||
|
KemX25519HkdfSha256 KemID = 0x0020
|
||||||
|
)
|
||||||
|
|
||||||
|
// KdfID - KDF ID
|
||||||
|
type KdfID uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
// KdfHkdfSha256 - HKDF-SHA256
|
||||||
|
KdfHkdfSha256 KdfID = 0x0001
|
||||||
|
)
|
||||||
|
|
||||||
|
// AeadID - AEAD ID
|
||||||
|
type AeadID uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AeadAes128Gcm - AES128-GCM
|
||||||
|
AeadAes128Gcm AeadID = 0x0001
|
||||||
|
// AeadAes256Gcm - AES256-GCM
|
||||||
|
AeadAes256Gcm AeadID = 0x0002
|
||||||
|
// AeadChaCha20Poly1305 - ChaCha20-Poly1305
|
||||||
|
AeadChaCha20Poly1305 AeadID = 0x0003
|
||||||
|
// AeadExportOnly - Don't use the HPKE encryption API
|
||||||
|
AeadExportOnly AeadID = 0xffff
|
||||||
|
)
|
||||||
|
|
||||||
|
// Psk - Pre-shared key and key ID
|
||||||
|
type Psk struct {
|
||||||
|
Key []byte
|
||||||
|
ID []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyPair - A key pair (packed as a byte string)
|
||||||
|
type KeyPair struct {
|
||||||
|
// PublicKey - Public key
|
||||||
|
PublicKey []byte
|
||||||
|
// SecretKey - Secret key
|
||||||
|
SecretKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type aeadState struct {
|
||||||
|
aead aeadImpl
|
||||||
|
baseNonce []byte
|
||||||
|
counter []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suite - HPKE suite
|
||||||
|
type Suite struct {
|
||||||
|
SuiteIDContext [10]byte
|
||||||
|
SuiteIDKEM [5]byte
|
||||||
|
Hash func() hash.Hash
|
||||||
|
PrkBytes uint16
|
||||||
|
KeyBytes uint16
|
||||||
|
NonceBytes uint16
|
||||||
|
KemHashBytes uint16
|
||||||
|
AeadID AeadID
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSuite - Create a new suite from its components
|
||||||
|
func NewSuite(kemID KemID, kdfID KdfID, aeadID AeadID) (*Suite, error) {
|
||||||
|
if kemID != KemX25519HkdfSha256 || kdfID != KdfHkdfSha256 {
|
||||||
|
return nil, errors.New("unimplemented suite")
|
||||||
|
}
|
||||||
|
hash := sha256.New
|
||||||
|
nonceBytes := uint16(12)
|
||||||
|
var keyBytes uint16
|
||||||
|
switch aeadID {
|
||||||
|
case AeadAes128Gcm:
|
||||||
|
keyBytes = 16
|
||||||
|
case AeadAes256Gcm:
|
||||||
|
keyBytes = 32
|
||||||
|
case AeadChaCha20Poly1305:
|
||||||
|
keyBytes = 32
|
||||||
|
case AeadExportOnly:
|
||||||
|
keyBytes = 0
|
||||||
|
nonceBytes = 0
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unimplemented suite")
|
||||||
|
}
|
||||||
|
var prkBytes uint16
|
||||||
|
switch kdfID {
|
||||||
|
case KdfHkdfSha256:
|
||||||
|
prkBytes = 32
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unimplemented suite")
|
||||||
|
}
|
||||||
|
var kemHashBytes uint16
|
||||||
|
switch kemID {
|
||||||
|
case KemX25519HkdfSha256:
|
||||||
|
kemHashBytes = 32
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unimplemented suite")
|
||||||
|
}
|
||||||
|
suite := Suite{
|
||||||
|
SuiteIDContext: getSuiteIDContext(kemID, kdfID, aeadID),
|
||||||
|
SuiteIDKEM: getSuiteIDKEM(kemID),
|
||||||
|
Hash: hash,
|
||||||
|
KeyBytes: keyBytes,
|
||||||
|
PrkBytes: prkBytes,
|
||||||
|
NonceBytes: nonceBytes,
|
||||||
|
KemHashBytes: kemHashBytes,
|
||||||
|
AeadID: aeadID,
|
||||||
|
}
|
||||||
|
return &suite, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSuiteIDContext(kemID KemID, kdfID KdfID, aeadID AeadID) [10]byte {
|
||||||
|
suiteIDContext := [10]byte{'H', 'P', 'K', 'E', 0, 0, 0, 0, 0, 0}
|
||||||
|
binary.BigEndian.PutUint16(suiteIDContext[4:6], uint16(kemID))
|
||||||
|
binary.BigEndian.PutUint16(suiteIDContext[6:8], uint16(kdfID))
|
||||||
|
binary.BigEndian.PutUint16(suiteIDContext[8:10], uint16(aeadID))
|
||||||
|
return suiteIDContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSuiteIDKEM(kemID KemID) [5]byte {
|
||||||
|
suiteIDKEM := [5]byte{'K', 'E', 'M', 0, 0}
|
||||||
|
binary.BigEndian.PutUint16(suiteIDKEM[3:5], uint16(kemID))
|
||||||
|
return suiteIDKEM
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract - KDF-Extract
|
||||||
|
func (suite *Suite) Extract(secret []byte, salt []byte) []byte {
|
||||||
|
return hkdf.Extract(suite.Hash, secret, salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand - KDF-Expand
|
||||||
|
func (suite *Suite) Expand(prk []byte, info []byte, length uint16) ([]byte, error) {
|
||||||
|
reader := hkdf.Expand(suite.Hash, prk, info)
|
||||||
|
out := make([]byte, length)
|
||||||
|
if readNb, err := reader.Read(out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if readNb != int(length) {
|
||||||
|
return nil, errors.New("unable to expand")
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) labeledExtract(suiteID []byte, salt []byte, label string, ikm []byte) []byte {
|
||||||
|
secret := append(hpkeVersion[:], suiteID...)
|
||||||
|
secret = append(secret, []byte(label)...)
|
||||||
|
secret = append(secret, ikm...)
|
||||||
|
return suite.Extract(secret, salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) labeledExpand(suiteID []byte, prk []byte, label string, info []byte, length uint16) ([]byte, error) {
|
||||||
|
labeledInfo := []byte{0, 0}
|
||||||
|
binary.BigEndian.PutUint16(labeledInfo, length)
|
||||||
|
labeledInfo = append(labeledInfo, hpkeVersion[:]...)
|
||||||
|
labeledInfo = append(labeledInfo, suiteID...)
|
||||||
|
labeledInfo = append(labeledInfo, []byte(label)...)
|
||||||
|
labeledInfo = append(labeledInfo, info...)
|
||||||
|
return suite.Expand(prk, labeledInfo, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) newAeadState(key []uint8, baseNonce []uint8) (*aeadState, error) {
|
||||||
|
var aead aeadImpl
|
||||||
|
var err error
|
||||||
|
switch suite.AeadID {
|
||||||
|
case AeadAes128Gcm, AeadAes256Gcm:
|
||||||
|
aead, err = newAesAead(key)
|
||||||
|
case AeadChaCha20Poly1305:
|
||||||
|
aead, err = newChaChaPolyAead(key)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unimplemented AEAD")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &aeadState{aead: aead, baseNonce: baseNonce, counter: make([]byte, suite.NonceBytes)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyPskInputs(mode Mode, psk *Psk) error {
|
||||||
|
if psk != nil && ((len(psk.Key) == 0) != (len(psk.ID) == 0)) {
|
||||||
|
return errors.New("a PSK and a PSK ID need both to be set")
|
||||||
|
}
|
||||||
|
if psk != nil {
|
||||||
|
if mode == ModeBase || mode == ModeAuth {
|
||||||
|
return errors.New("PSK input provided when not needed")
|
||||||
|
}
|
||||||
|
} else if mode == ModePsk || mode == ModeAuthPsk {
|
||||||
|
return errors.New("PSK required for that mode")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// innerContext - An AEAD context
|
||||||
|
type innerContext struct {
|
||||||
|
suite *Suite
|
||||||
|
exporterSecret []byte
|
||||||
|
outboundState *aeadState
|
||||||
|
inboundState *aeadState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *innerContext) export(exporterContext []byte, length uint16) ([]byte, error) {
|
||||||
|
return inner.suite.labeledExpand(inner.suite.SuiteIDContext[:], inner.exporterSecret, "sec", exporterContext, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientContext - A client encryption context
|
||||||
|
type ClientContext struct {
|
||||||
|
inner innerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerContext - A server encryption context
|
||||||
|
type ServerContext struct {
|
||||||
|
inner innerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) keySchedule(mode Mode, dhSecret []byte, info []byte, psk *Psk) (innerContext, error) {
|
||||||
|
if err := verifyPskInputs(mode, psk); err != nil {
|
||||||
|
return innerContext{}, err
|
||||||
|
}
|
||||||
|
if psk == nil {
|
||||||
|
psk = &Psk{}
|
||||||
|
}
|
||||||
|
pskIDHash := suite.labeledExtract(suite.SuiteIDContext[:], nil, "psk_id_hash", psk.ID)
|
||||||
|
infoHash := suite.labeledExtract(suite.SuiteIDContext[:], nil, "info_hash", info)
|
||||||
|
keyScheduleContext := []byte{byte(mode)}
|
||||||
|
keyScheduleContext = append(keyScheduleContext, pskIDHash...)
|
||||||
|
keyScheduleContext = append(keyScheduleContext, infoHash...)
|
||||||
|
secret := suite.labeledExtract(suite.SuiteIDContext[:], dhSecret, "secret", psk.Key)
|
||||||
|
|
||||||
|
exporterSecret, err := suite.labeledExpand(suite.SuiteIDContext[:], secret, "exp", keyScheduleContext, suite.PrkBytes)
|
||||||
|
if err != nil {
|
||||||
|
return innerContext{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var outboundState *aeadState
|
||||||
|
if suite.AeadID != AeadExportOnly {
|
||||||
|
outboundKey, err := suite.labeledExpand(suite.SuiteIDContext[:], secret, "key", keyScheduleContext, suite.KeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return innerContext{}, err
|
||||||
|
}
|
||||||
|
outboundBaseNonce, err := suite.labeledExpand(suite.SuiteIDContext[:], secret, "base_nonce", keyScheduleContext, suite.NonceBytes)
|
||||||
|
if err != nil {
|
||||||
|
return innerContext{}, err
|
||||||
|
}
|
||||||
|
outboundState, err = suite.newAeadState(outboundKey, outboundBaseNonce)
|
||||||
|
if err != nil {
|
||||||
|
return innerContext{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return innerContext{
|
||||||
|
suite: suite,
|
||||||
|
exporterSecret: exporterSecret,
|
||||||
|
outboundState: outboundState,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKeyPair - Generate a random key pair
|
||||||
|
func (suite *Suite) GenerateKeyPair() (KeyPair, error) {
|
||||||
|
var pk, sk [32]byte
|
||||||
|
if _, err := crypto_rand.Read(sk[:]); err != nil {
|
||||||
|
return KeyPair{}, err
|
||||||
|
}
|
||||||
|
curve25519.ScalarBaseMult(&pk, &sk)
|
||||||
|
return KeyPair{PublicKey: pk[:], SecretKey: sk[:]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeterministicKeyPair - Derive a deterministic key pair from a seed
|
||||||
|
func (suite *Suite) DeterministicKeyPair(seed []byte) (KeyPair, error) {
|
||||||
|
var pk, sk [32]byte
|
||||||
|
prk := suite.labeledExtract(suite.SuiteIDKEM[:], nil, "dkp_prk", seed)
|
||||||
|
xsk, err := suite.labeledExpand(suite.SuiteIDKEM[:], prk, "sk", nil, 32)
|
||||||
|
if err != nil {
|
||||||
|
return KeyPair{}, err
|
||||||
|
}
|
||||||
|
copy(sk[:], xsk)
|
||||||
|
curve25519.ScalarBaseMult(&pk, &sk)
|
||||||
|
return KeyPair{PublicKey: pk[:], SecretKey: sk[:]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) dh(pk []byte, sk []byte) ([]byte, error) {
|
||||||
|
dhSecret, err := curve25519.X25519(sk, pk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dhSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) extractAndExpandDH(dh []byte, kemContext []byte) ([]byte, error) {
|
||||||
|
prk := suite.labeledExtract(suite.SuiteIDKEM[:], nil, "eae_prk", dh)
|
||||||
|
dhSecret, err := suite.labeledExpand(suite.SuiteIDKEM[:], prk, "shared_secret", kemContext, suite.KemHashBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dhSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) encap(serverPk []byte, seed []byte) ([]byte, []byte, error) {
|
||||||
|
var ephKp KeyPair
|
||||||
|
var err error
|
||||||
|
if len(seed) > 0 {
|
||||||
|
ephKp, err = suite.DeterministicKeyPair(seed)
|
||||||
|
} else {
|
||||||
|
ephKp, err = suite.GenerateKeyPair()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
dh, err := suite.dh(serverPk, ephKp.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
kemContext := append(ephKp.PublicKey, serverPk...)
|
||||||
|
dhSecret, err := suite.extractAndExpandDH(dh, kemContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return dhSecret, ephKp.PublicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) decap(ephPk []byte, serverKp KeyPair) ([]byte, error) {
|
||||||
|
dh, err := suite.dh(ephPk, serverKp.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
kemContext := append(ephPk, serverKp.PublicKey...)
|
||||||
|
dhSecret, err := suite.extractAndExpandDH(dh, kemContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dhSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) authEncap(serverPk []byte, clientKp KeyPair, seed []byte) ([]byte, []byte, error) {
|
||||||
|
var ephKp KeyPair
|
||||||
|
var err error
|
||||||
|
if len(seed) > 0 {
|
||||||
|
ephKp, err = suite.DeterministicKeyPair(seed)
|
||||||
|
} else {
|
||||||
|
ephKp, err = suite.GenerateKeyPair()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
dh1, err := suite.dh(serverPk, ephKp.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
dh2, err := suite.dh(serverPk, clientKp.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
dh := append(dh1, dh2...)
|
||||||
|
kemContext := append(ephKp.PublicKey, serverPk...)
|
||||||
|
kemContext = append(kemContext, clientKp.PublicKey...)
|
||||||
|
dhSecret, err := suite.extractAndExpandDH(dh, kemContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return dhSecret, ephKp.PublicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) authDecap(ephPk []byte, serverKp KeyPair, clientPk []byte) ([]byte, error) {
|
||||||
|
dh1, err := suite.dh(ephPk, serverKp.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dh2, err := suite.dh(clientPk, serverKp.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dh := append(dh1, dh2...)
|
||||||
|
kemContext := append(ephPk, serverKp.PublicKey...)
|
||||||
|
kemContext = append(kemContext, clientPk...)
|
||||||
|
dhSecret, err := suite.extractAndExpandDH(dh, kemContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dhSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientContext - Create a new context for a client (aka "sender")
|
||||||
|
func (suite *Suite) NewClientContext(serverPk []byte, info []byte, psk *Psk) (ClientContext, []byte, error) {
|
||||||
|
dhSecret, enc, err := suite.encap(serverPk, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
mode := ModeBase
|
||||||
|
if psk != nil {
|
||||||
|
mode = ModePsk
|
||||||
|
}
|
||||||
|
context, err := suite.keySchedule(mode, dhSecret, info, psk)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
return ClientContext{inner: context}, enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientDeterministicContext - Create a new deterministic context for a client - Should only be used for testing purposes
|
||||||
|
func (suite *Suite) NewClientDeterministicContext(serverPk []byte, info []byte, psk *Psk, seed []byte) (ClientContext, []byte, error) {
|
||||||
|
dhSecret, enc, err := suite.encap(serverPk, seed)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
mode := ModeBase
|
||||||
|
if psk != nil {
|
||||||
|
mode = ModePsk
|
||||||
|
}
|
||||||
|
context, err := suite.keySchedule(mode, dhSecret, info, psk)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
return ClientContext{inner: context}, enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerContext - Create a new context for a server (aka "recipient")
|
||||||
|
func (suite *Suite) NewServerContext(enc []byte, serverKp KeyPair, info []byte, psk *Psk) (ServerContext, error) {
|
||||||
|
dhSecret, err := suite.decap(enc, serverKp)
|
||||||
|
if err != nil {
|
||||||
|
return ServerContext{}, err
|
||||||
|
}
|
||||||
|
mode := ModeBase
|
||||||
|
if psk != nil {
|
||||||
|
mode = ModePsk
|
||||||
|
}
|
||||||
|
context, err := suite.keySchedule(mode, dhSecret, info, psk)
|
||||||
|
if err != nil {
|
||||||
|
return ServerContext{}, err
|
||||||
|
}
|
||||||
|
return ServerContext{inner: context}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthenticatedClientContext - Create a new context for a client (aka "sender"), with authentication
|
||||||
|
func (suite *Suite) NewAuthenticatedClientContext(clientKp KeyPair, serverPk []byte, info []byte, psk *Psk) (ClientContext, []byte, error) {
|
||||||
|
dhSecret, enc, err := suite.authEncap(serverPk, clientKp, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
mode := ModeAuth
|
||||||
|
if psk != nil {
|
||||||
|
mode = ModeAuthPsk
|
||||||
|
}
|
||||||
|
context, err := suite.keySchedule(mode, dhSecret, info, psk)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
return ClientContext{inner: context}, enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthenticatedClientDeterministicContext - Create a new deterministic context for a client, with authentication - Should only be used for testing purposes
|
||||||
|
func (suite *Suite) NewAuthenticatedClientDeterministicContext(clientKp KeyPair, serverPk []byte, info []byte, psk *Psk, seed []byte) (ClientContext, []byte, error) {
|
||||||
|
dhSecret, enc, err := suite.authEncap(serverPk, clientKp, seed)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
mode := ModeAuth
|
||||||
|
if psk != nil {
|
||||||
|
mode = ModeAuthPsk
|
||||||
|
}
|
||||||
|
context, err := suite.keySchedule(mode, dhSecret, info, psk)
|
||||||
|
if err != nil {
|
||||||
|
return ClientContext{}, nil, err
|
||||||
|
}
|
||||||
|
return ClientContext{inner: context}, enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthenticatedServerContext - Create a new context for a server (aka "recipient"), with authentication
|
||||||
|
func (suite *Suite) NewAuthenticatedServerContext(clientPk []byte, enc []byte, serverKp KeyPair, info []byte, psk *Psk) (ServerContext, error) {
|
||||||
|
dhSecret, err := suite.authDecap(enc, serverKp, clientPk)
|
||||||
|
if err != nil {
|
||||||
|
return ServerContext{}, err
|
||||||
|
}
|
||||||
|
mode := ModeAuth
|
||||||
|
if psk != nil {
|
||||||
|
mode = ModeAuthPsk
|
||||||
|
}
|
||||||
|
context, err := suite.keySchedule(mode, dhSecret, info, psk)
|
||||||
|
if err != nil {
|
||||||
|
return ServerContext{}, err
|
||||||
|
}
|
||||||
|
return ServerContext{inner: context}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *aeadState) incrementCounter() error {
|
||||||
|
carry := uint16(1)
|
||||||
|
for i := len(state.counter); ; {
|
||||||
|
i--
|
||||||
|
x := uint16(state.counter[i]) + carry
|
||||||
|
state.counter[i] = byte(x & 0xff)
|
||||||
|
carry = x >> 8
|
||||||
|
if i == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if carry != 0 {
|
||||||
|
return errors.New("Overflow")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextNonce - Get the next nonce to encrypt/decrypt a message with an AEAD
|
||||||
|
// Note: this is not thread-safe.
|
||||||
|
func (state *aeadState) NextNonce() []byte {
|
||||||
|
if len(state.counter) != len(state.baseNonce) {
|
||||||
|
panic("Inconsistent nonce length")
|
||||||
|
}
|
||||||
|
nonce := append(state.baseNonce[:0:0], state.baseNonce...)
|
||||||
|
for i := 0; i < len(nonce); i++ {
|
||||||
|
nonce[i] ^= state.counter[i]
|
||||||
|
}
|
||||||
|
state.incrementCounter()
|
||||||
|
return nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptToServer - Encrypt and authenticate a message for the server, with optional associated data
|
||||||
|
func (context *ClientContext) EncryptToServer(message []byte, ad []byte) ([]byte, error) {
|
||||||
|
state := context.inner.outboundState
|
||||||
|
nonce := state.NextNonce()
|
||||||
|
return state.aead.internal().Seal(nil, nonce, message, ad), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptFromClient - Verify and decrypt a ciphertext received from the client, with optional associated data
|
||||||
|
func (context *ServerContext) DecryptFromClient(ciphertext []byte, ad []byte) ([]byte, error) {
|
||||||
|
state := context.inner.outboundState
|
||||||
|
nonce := state.NextNonce()
|
||||||
|
return state.aead.internal().Open(nil, nonce, ciphertext, ad)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *innerContext) responseState() (*aeadState, error) {
|
||||||
|
key, err := inner.export([]byte("response key"), inner.suite.KeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
baseNonce, err := inner.export([]byte("response nonce"), inner.suite.NonceBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return inner.suite.newAeadState(key, baseNonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptToClient - Encrypt and authenticate a message for the client, with optional associated data
|
||||||
|
func (context *ServerContext) EncryptToClient(message []byte, ad []byte) ([]byte, error) {
|
||||||
|
if context.inner.inboundState == nil {
|
||||||
|
var err error
|
||||||
|
context.inner.inboundState, err = context.inner.responseState()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state := context.inner.inboundState
|
||||||
|
nonce := state.NextNonce()
|
||||||
|
return state.aead.internal().Seal(nil, nonce, message, ad), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptFromServer - Verify and decrypt a ciphertext received from the server, with optional associated data
|
||||||
|
func (context *ClientContext) DecryptFromServer(ciphertext []byte, ad []byte) ([]byte, error) {
|
||||||
|
if context.inner.inboundState == nil {
|
||||||
|
var err error
|
||||||
|
context.inner.inboundState, err = context.inner.responseState()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state := context.inner.inboundState
|
||||||
|
nonce := state.NextNonce()
|
||||||
|
return state.aead.internal().Open(nil, nonce, ciphertext, ad)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExporterSecret - Return the exporter secret
|
||||||
|
func (context *ClientContext) ExporterSecret() []byte {
|
||||||
|
return context.inner.exporterSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExporterSecret - Return the exporter secret
|
||||||
|
func (context *ServerContext) ExporterSecret() []byte {
|
||||||
|
return context.inner.exporterSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export - Return the exporter secret
|
||||||
|
func (context *ClientContext) Export(exporterContext []byte, length uint16) ([]byte, error) {
|
||||||
|
return context.inner.export(exporterContext, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export - Return the exporter secret
|
||||||
|
func (context *ServerContext) Export(exporterContext []byte, length uint16) ([]byte, error) {
|
||||||
|
return context.inner.export(exporterContext, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
type aeadImpl interface {
|
||||||
|
internal() cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
type aeadAesImpl struct {
|
||||||
|
impl cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAesAead(key []byte) (aeadAesImpl, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return aeadAesImpl{}, nil
|
||||||
|
}
|
||||||
|
aesGcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return aeadAesImpl{}, nil
|
||||||
|
}
|
||||||
|
aead := aeadAesImpl{impl: aesGcm}
|
||||||
|
return aead, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aead aeadAesImpl) internal() cipher.AEAD {
|
||||||
|
return aead.impl
|
||||||
|
}
|
||||||
|
|
||||||
|
type aeadChaChaPolyImpl struct {
|
||||||
|
impl cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChaChaPolyAead(key []byte) (aeadChaChaPolyImpl, error) {
|
||||||
|
impl, err := chacha20poly1305.New(key)
|
||||||
|
if err != nil {
|
||||||
|
return aeadChaChaPolyImpl{}, nil
|
||||||
|
}
|
||||||
|
aead := aeadChaChaPolyImpl{impl: impl}
|
||||||
|
return aead, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aead aeadChaChaPolyImpl) internal() cipher.AEAD {
|
||||||
|
return aead.impl
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2018 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.11 && gc && !purego
|
||||||
|
// +build go1.11,gc,!purego
|
||||||
|
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
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) {
|
||||||
|
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||||
|
}
|
|
@ -0,0 +1,307 @@
|
||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
// +build go1.11,gc,!purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
#define NUM_ROUNDS 10
|
||||||
|
|
||||||
|
// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||||
|
TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0
|
||||||
|
MOVD dst+0(FP), R1
|
||||||
|
MOVD src+24(FP), R2
|
||||||
|
MOVD src_len+32(FP), R3
|
||||||
|
MOVD key+48(FP), R4
|
||||||
|
MOVD nonce+56(FP), R6
|
||||||
|
MOVD counter+64(FP), R7
|
||||||
|
|
||||||
|
MOVD $·constants(SB), R10
|
||||||
|
MOVD $·incRotMatrix(SB), R11
|
||||||
|
|
||||||
|
MOVW (R7), R20
|
||||||
|
|
||||||
|
AND $~255, R3, R13
|
||||||
|
ADD R2, R13, R12 // R12 for block end
|
||||||
|
AND $255, R3, R13
|
||||||
|
loop:
|
||||||
|
MOVD $NUM_ROUNDS, R21
|
||||||
|
VLD1 (R11), [V30.S4, V31.S4]
|
||||||
|
|
||||||
|
// load contants
|
||||||
|
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4]
|
||||||
|
WORD $0x4D60E940
|
||||||
|
|
||||||
|
// load keys
|
||||||
|
// VLD4R 16(R4), [V4.S4, V5.S4, V6.S4, V7.S4]
|
||||||
|
WORD $0x4DFFE884
|
||||||
|
// VLD4R 16(R4), [V8.S4, V9.S4, V10.S4, V11.S4]
|
||||||
|
WORD $0x4DFFE888
|
||||||
|
SUB $32, R4
|
||||||
|
|
||||||
|
// load counter + nonce
|
||||||
|
// VLD1R (R7), [V12.S4]
|
||||||
|
WORD $0x4D40C8EC
|
||||||
|
|
||||||
|
// VLD3R (R6), [V13.S4, V14.S4, V15.S4]
|
||||||
|
WORD $0x4D40E8CD
|
||||||
|
|
||||||
|
// update counter
|
||||||
|
VADD V30.S4, V12.S4, V12.S4
|
||||||
|
|
||||||
|
chacha:
|
||||||
|
// V0..V3 += V4..V7
|
||||||
|
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 16)
|
||||||
|
VADD V0.S4, V4.S4, V0.S4
|
||||||
|
VADD V1.S4, V5.S4, V1.S4
|
||||||
|
VADD V2.S4, V6.S4, V2.S4
|
||||||
|
VADD V3.S4, V7.S4, V3.S4
|
||||||
|
VEOR V12.B16, V0.B16, V12.B16
|
||||||
|
VEOR V13.B16, V1.B16, V13.B16
|
||||||
|
VEOR V14.B16, V2.B16, V14.B16
|
||||||
|
VEOR V15.B16, V3.B16, V15.B16
|
||||||
|
VREV32 V12.H8, V12.H8
|
||||||
|
VREV32 V13.H8, V13.H8
|
||||||
|
VREV32 V14.H8, V14.H8
|
||||||
|
VREV32 V15.H8, V15.H8
|
||||||
|
// V8..V11 += V12..V15
|
||||||
|
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 12)
|
||||||
|
VADD V8.S4, V12.S4, V8.S4
|
||||||
|
VADD V9.S4, V13.S4, V9.S4
|
||||||
|
VADD V10.S4, V14.S4, V10.S4
|
||||||
|
VADD V11.S4, V15.S4, V11.S4
|
||||||
|
VEOR V8.B16, V4.B16, V16.B16
|
||||||
|
VEOR V9.B16, V5.B16, V17.B16
|
||||||
|
VEOR V10.B16, V6.B16, V18.B16
|
||||||
|
VEOR V11.B16, V7.B16, V19.B16
|
||||||
|
VSHL $12, V16.S4, V4.S4
|
||||||
|
VSHL $12, V17.S4, V5.S4
|
||||||
|
VSHL $12, V18.S4, V6.S4
|
||||||
|
VSHL $12, V19.S4, V7.S4
|
||||||
|
VSRI $20, V16.S4, V4.S4
|
||||||
|
VSRI $20, V17.S4, V5.S4
|
||||||
|
VSRI $20, V18.S4, V6.S4
|
||||||
|
VSRI $20, V19.S4, V7.S4
|
||||||
|
|
||||||
|
// V0..V3 += V4..V7
|
||||||
|
// V12..V15 <<<= ((V12..V15 XOR V0..V3), 8)
|
||||||
|
VADD V0.S4, V4.S4, V0.S4
|
||||||
|
VADD V1.S4, V5.S4, V1.S4
|
||||||
|
VADD V2.S4, V6.S4, V2.S4
|
||||||
|
VADD V3.S4, V7.S4, V3.S4
|
||||||
|
VEOR V12.B16, V0.B16, V12.B16
|
||||||
|
VEOR V13.B16, V1.B16, V13.B16
|
||||||
|
VEOR V14.B16, V2.B16, V14.B16
|
||||||
|
VEOR V15.B16, V3.B16, V15.B16
|
||||||
|
VTBL V31.B16, [V12.B16], V12.B16
|
||||||
|
VTBL V31.B16, [V13.B16], V13.B16
|
||||||
|
VTBL V31.B16, [V14.B16], V14.B16
|
||||||
|
VTBL V31.B16, [V15.B16], V15.B16
|
||||||
|
|
||||||
|
// V8..V11 += V12..V15
|
||||||
|
// V4..V7 <<<= ((V4..V7 XOR V8..V11), 7)
|
||||||
|
VADD V12.S4, V8.S4, V8.S4
|
||||||
|
VADD V13.S4, V9.S4, V9.S4
|
||||||
|
VADD V14.S4, V10.S4, V10.S4
|
||||||
|
VADD V15.S4, V11.S4, V11.S4
|
||||||
|
VEOR V8.B16, V4.B16, V16.B16
|
||||||
|
VEOR V9.B16, V5.B16, V17.B16
|
||||||
|
VEOR V10.B16, V6.B16, V18.B16
|
||||||
|
VEOR V11.B16, V7.B16, V19.B16
|
||||||
|
VSHL $7, V16.S4, V4.S4
|
||||||
|
VSHL $7, V17.S4, V5.S4
|
||||||
|
VSHL $7, V18.S4, V6.S4
|
||||||
|
VSHL $7, V19.S4, V7.S4
|
||||||
|
VSRI $25, V16.S4, V4.S4
|
||||||
|
VSRI $25, V17.S4, V5.S4
|
||||||
|
VSRI $25, V18.S4, V6.S4
|
||||||
|
VSRI $25, V19.S4, V7.S4
|
||||||
|
|
||||||
|
// V0..V3 += V5..V7, V4
|
||||||
|
// V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16)
|
||||||
|
VADD V0.S4, V5.S4, V0.S4
|
||||||
|
VADD V1.S4, V6.S4, V1.S4
|
||||||
|
VADD V2.S4, V7.S4, V2.S4
|
||||||
|
VADD V3.S4, V4.S4, V3.S4
|
||||||
|
VEOR V15.B16, V0.B16, V15.B16
|
||||||
|
VEOR V12.B16, V1.B16, V12.B16
|
||||||
|
VEOR V13.B16, V2.B16, V13.B16
|
||||||
|
VEOR V14.B16, V3.B16, V14.B16
|
||||||
|
VREV32 V12.H8, V12.H8
|
||||||
|
VREV32 V13.H8, V13.H8
|
||||||
|
VREV32 V14.H8, V14.H8
|
||||||
|
VREV32 V15.H8, V15.H8
|
||||||
|
|
||||||
|
// V10 += V15; V5 <<<= ((V10 XOR V5), 12)
|
||||||
|
// ...
|
||||||
|
VADD V15.S4, V10.S4, V10.S4
|
||||||
|
VADD V12.S4, V11.S4, V11.S4
|
||||||
|
VADD V13.S4, V8.S4, V8.S4
|
||||||
|
VADD V14.S4, V9.S4, V9.S4
|
||||||
|
VEOR V10.B16, V5.B16, V16.B16
|
||||||
|
VEOR V11.B16, V6.B16, V17.B16
|
||||||
|
VEOR V8.B16, V7.B16, V18.B16
|
||||||
|
VEOR V9.B16, V4.B16, V19.B16
|
||||||
|
VSHL $12, V16.S4, V5.S4
|
||||||
|
VSHL $12, V17.S4, V6.S4
|
||||||
|
VSHL $12, V18.S4, V7.S4
|
||||||
|
VSHL $12, V19.S4, V4.S4
|
||||||
|
VSRI $20, V16.S4, V5.S4
|
||||||
|
VSRI $20, V17.S4, V6.S4
|
||||||
|
VSRI $20, V18.S4, V7.S4
|
||||||
|
VSRI $20, V19.S4, V4.S4
|
||||||
|
|
||||||
|
// V0 += V5; V15 <<<= ((V0 XOR V15), 8)
|
||||||
|
// ...
|
||||||
|
VADD V5.S4, V0.S4, V0.S4
|
||||||
|
VADD V6.S4, V1.S4, V1.S4
|
||||||
|
VADD V7.S4, V2.S4, V2.S4
|
||||||
|
VADD V4.S4, V3.S4, V3.S4
|
||||||
|
VEOR V0.B16, V15.B16, V15.B16
|
||||||
|
VEOR V1.B16, V12.B16, V12.B16
|
||||||
|
VEOR V2.B16, V13.B16, V13.B16
|
||||||
|
VEOR V3.B16, V14.B16, V14.B16
|
||||||
|
VTBL V31.B16, [V12.B16], V12.B16
|
||||||
|
VTBL V31.B16, [V13.B16], V13.B16
|
||||||
|
VTBL V31.B16, [V14.B16], V14.B16
|
||||||
|
VTBL V31.B16, [V15.B16], V15.B16
|
||||||
|
|
||||||
|
// V10 += V15; V5 <<<= ((V10 XOR V5), 7)
|
||||||
|
// ...
|
||||||
|
VADD V15.S4, V10.S4, V10.S4
|
||||||
|
VADD V12.S4, V11.S4, V11.S4
|
||||||
|
VADD V13.S4, V8.S4, V8.S4
|
||||||
|
VADD V14.S4, V9.S4, V9.S4
|
||||||
|
VEOR V10.B16, V5.B16, V16.B16
|
||||||
|
VEOR V11.B16, V6.B16, V17.B16
|
||||||
|
VEOR V8.B16, V7.B16, V18.B16
|
||||||
|
VEOR V9.B16, V4.B16, V19.B16
|
||||||
|
VSHL $7, V16.S4, V5.S4
|
||||||
|
VSHL $7, V17.S4, V6.S4
|
||||||
|
VSHL $7, V18.S4, V7.S4
|
||||||
|
VSHL $7, V19.S4, V4.S4
|
||||||
|
VSRI $25, V16.S4, V5.S4
|
||||||
|
VSRI $25, V17.S4, V6.S4
|
||||||
|
VSRI $25, V18.S4, V7.S4
|
||||||
|
VSRI $25, V19.S4, V4.S4
|
||||||
|
|
||||||
|
SUB $1, R21
|
||||||
|
CBNZ R21, chacha
|
||||||
|
|
||||||
|
// VLD4R (R10), [V16.S4, V17.S4, V18.S4, V19.S4]
|
||||||
|
WORD $0x4D60E950
|
||||||
|
|
||||||
|
// VLD4R 16(R4), [V20.S4, V21.S4, V22.S4, V23.S4]
|
||||||
|
WORD $0x4DFFE894
|
||||||
|
VADD V30.S4, V12.S4, V12.S4
|
||||||
|
VADD V16.S4, V0.S4, V0.S4
|
||||||
|
VADD V17.S4, V1.S4, V1.S4
|
||||||
|
VADD V18.S4, V2.S4, V2.S4
|
||||||
|
VADD V19.S4, V3.S4, V3.S4
|
||||||
|
// VLD4R 16(R4), [V24.S4, V25.S4, V26.S4, V27.S4]
|
||||||
|
WORD $0x4DFFE898
|
||||||
|
// restore R4
|
||||||
|
SUB $32, R4
|
||||||
|
|
||||||
|
// load counter + nonce
|
||||||
|
// VLD1R (R7), [V28.S4]
|
||||||
|
WORD $0x4D40C8FC
|
||||||
|
// VLD3R (R6), [V29.S4, V30.S4, V31.S4]
|
||||||
|
WORD $0x4D40E8DD
|
||||||
|
|
||||||
|
VADD V20.S4, V4.S4, V4.S4
|
||||||
|
VADD V21.S4, V5.S4, V5.S4
|
||||||
|
VADD V22.S4, V6.S4, V6.S4
|
||||||
|
VADD V23.S4, V7.S4, V7.S4
|
||||||
|
VADD V24.S4, V8.S4, V8.S4
|
||||||
|
VADD V25.S4, V9.S4, V9.S4
|
||||||
|
VADD V26.S4, V10.S4, V10.S4
|
||||||
|
VADD V27.S4, V11.S4, V11.S4
|
||||||
|
VADD V28.S4, V12.S4, V12.S4
|
||||||
|
VADD V29.S4, V13.S4, V13.S4
|
||||||
|
VADD V30.S4, V14.S4, V14.S4
|
||||||
|
VADD V31.S4, V15.S4, V15.S4
|
||||||
|
|
||||||
|
VZIP1 V1.S4, V0.S4, V16.S4
|
||||||
|
VZIP2 V1.S4, V0.S4, V17.S4
|
||||||
|
VZIP1 V3.S4, V2.S4, V18.S4
|
||||||
|
VZIP2 V3.S4, V2.S4, V19.S4
|
||||||
|
VZIP1 V5.S4, V4.S4, V20.S4
|
||||||
|
VZIP2 V5.S4, V4.S4, V21.S4
|
||||||
|
VZIP1 V7.S4, V6.S4, V22.S4
|
||||||
|
VZIP2 V7.S4, V6.S4, V23.S4
|
||||||
|
VZIP1 V9.S4, V8.S4, V24.S4
|
||||||
|
VZIP2 V9.S4, V8.S4, V25.S4
|
||||||
|
VZIP1 V11.S4, V10.S4, V26.S4
|
||||||
|
VZIP2 V11.S4, V10.S4, V27.S4
|
||||||
|
VZIP1 V13.S4, V12.S4, V28.S4
|
||||||
|
VZIP2 V13.S4, V12.S4, V29.S4
|
||||||
|
VZIP1 V15.S4, V14.S4, V30.S4
|
||||||
|
VZIP2 V15.S4, V14.S4, V31.S4
|
||||||
|
VZIP1 V18.D2, V16.D2, V0.D2
|
||||||
|
VZIP2 V18.D2, V16.D2, V4.D2
|
||||||
|
VZIP1 V19.D2, V17.D2, V8.D2
|
||||||
|
VZIP2 V19.D2, V17.D2, V12.D2
|
||||||
|
VLD1.P 64(R2), [V16.B16, V17.B16, V18.B16, V19.B16]
|
||||||
|
|
||||||
|
VZIP1 V22.D2, V20.D2, V1.D2
|
||||||
|
VZIP2 V22.D2, V20.D2, V5.D2
|
||||||
|
VZIP1 V23.D2, V21.D2, V9.D2
|
||||||
|
VZIP2 V23.D2, V21.D2, V13.D2
|
||||||
|
VLD1.P 64(R2), [V20.B16, V21.B16, V22.B16, V23.B16]
|
||||||
|
VZIP1 V26.D2, V24.D2, V2.D2
|
||||||
|
VZIP2 V26.D2, V24.D2, V6.D2
|
||||||
|
VZIP1 V27.D2, V25.D2, V10.D2
|
||||||
|
VZIP2 V27.D2, V25.D2, V14.D2
|
||||||
|
VLD1.P 64(R2), [V24.B16, V25.B16, V26.B16, V27.B16]
|
||||||
|
VZIP1 V30.D2, V28.D2, V3.D2
|
||||||
|
VZIP2 V30.D2, V28.D2, V7.D2
|
||||||
|
VZIP1 V31.D2, V29.D2, V11.D2
|
||||||
|
VZIP2 V31.D2, V29.D2, V15.D2
|
||||||
|
VLD1.P 64(R2), [V28.B16, V29.B16, V30.B16, V31.B16]
|
||||||
|
VEOR V0.B16, V16.B16, V16.B16
|
||||||
|
VEOR V1.B16, V17.B16, V17.B16
|
||||||
|
VEOR V2.B16, V18.B16, V18.B16
|
||||||
|
VEOR V3.B16, V19.B16, V19.B16
|
||||||
|
VST1.P [V16.B16, V17.B16, V18.B16, V19.B16], 64(R1)
|
||||||
|
VEOR V4.B16, V20.B16, V20.B16
|
||||||
|
VEOR V5.B16, V21.B16, V21.B16
|
||||||
|
VEOR V6.B16, V22.B16, V22.B16
|
||||||
|
VEOR V7.B16, V23.B16, V23.B16
|
||||||
|
VST1.P [V20.B16, V21.B16, V22.B16, V23.B16], 64(R1)
|
||||||
|
VEOR V8.B16, V24.B16, V24.B16
|
||||||
|
VEOR V9.B16, V25.B16, V25.B16
|
||||||
|
VEOR V10.B16, V26.B16, V26.B16
|
||||||
|
VEOR V11.B16, V27.B16, V27.B16
|
||||||
|
VST1.P [V24.B16, V25.B16, V26.B16, V27.B16], 64(R1)
|
||||||
|
VEOR V12.B16, V28.B16, V28.B16
|
||||||
|
VEOR V13.B16, V29.B16, V29.B16
|
||||||
|
VEOR V14.B16, V30.B16, V30.B16
|
||||||
|
VEOR V15.B16, V31.B16, V31.B16
|
||||||
|
VST1.P [V28.B16, V29.B16, V30.B16, V31.B16], 64(R1)
|
||||||
|
|
||||||
|
ADD $4, R20
|
||||||
|
MOVW R20, (R7) // update counter
|
||||||
|
|
||||||
|
CMP R2, R12
|
||||||
|
BGT loop
|
||||||
|
|
||||||
|
RET
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
DATA ·incRotMatrix+0x10(SB)/4, $0x02010003
|
||||||
|
DATA ·incRotMatrix+0x14(SB)/4, $0x06050407
|
||||||
|
DATA ·incRotMatrix+0x18(SB)/4, $0x0A09080B
|
||||||
|
DATA ·incRotMatrix+0x1c(SB)/4, $0x0E0D0C0F
|
||||||
|
GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32
|
|
@ -0,0 +1,398 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package chacha20 implements the ChaCha20 and XChaCha20 encryption algorithms
|
||||||
|
// as specified in RFC 8439 and draft-irtf-cfrg-xchacha-01.
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/internal/subtle"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// KeySize is the size of the key used by this cipher, in bytes.
|
||||||
|
KeySize = 32
|
||||||
|
|
||||||
|
// NonceSize is the size of the nonce used with the standard variant of this
|
||||||
|
// cipher, in bytes.
|
||||||
|
//
|
||||||
|
// Note that this is too short to be safely generated at random if the same
|
||||||
|
// key is reused more than 2³² times.
|
||||||
|
NonceSize = 12
|
||||||
|
|
||||||
|
// NonceSizeX is the size of the nonce used with the XChaCha20 variant of
|
||||||
|
// this cipher, in bytes.
|
||||||
|
NonceSizeX = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cipher is a stateful instance of ChaCha20 or XChaCha20 using a particular key
|
||||||
|
// and nonce. A *Cipher implements the cipher.Stream interface.
|
||||||
|
type Cipher struct {
|
||||||
|
// The ChaCha20 state is 16 words: 4 constant, 8 of key, 1 of counter
|
||||||
|
// (incremented after each block), and 3 of nonce.
|
||||||
|
key [8]uint32
|
||||||
|
counter uint32
|
||||||
|
nonce [3]uint32
|
||||||
|
|
||||||
|
// The last len bytes of buf are leftover key stream bytes from the previous
|
||||||
|
// XORKeyStream invocation. The size of buf depends on how many blocks are
|
||||||
|
// computed at a time by xorKeyStreamBlocks.
|
||||||
|
buf [bufSize]byte
|
||||||
|
len int
|
||||||
|
|
||||||
|
// overflow is set when the counter overflowed, no more blocks can be
|
||||||
|
// generated, and the next XORKeyStream call should panic.
|
||||||
|
overflow bool
|
||||||
|
|
||||||
|
// The counter-independent results of the first round are cached after they
|
||||||
|
// are computed the first time.
|
||||||
|
precompDone bool
|
||||||
|
p1, p5, p9, p13 uint32
|
||||||
|
p2, p6, p10, p14 uint32
|
||||||
|
p3, p7, p11, p15 uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ cipher.Stream = (*Cipher)(nil)
|
||||||
|
|
||||||
|
// NewUnauthenticatedCipher creates a new ChaCha20 stream cipher with the given
|
||||||
|
// 32 bytes key and a 12 or 24 bytes nonce. If a nonce of 24 bytes is provided,
|
||||||
|
// the XChaCha20 construction will be used. It returns an error if key or nonce
|
||||||
|
// have any other length.
|
||||||
|
//
|
||||||
|
// Note that ChaCha20, like all stream ciphers, is not authenticated and allows
|
||||||
|
// attackers to silently tamper with the plaintext. For this reason, it is more
|
||||||
|
// appropriate as a building block than as a standalone encryption mechanism.
|
||||||
|
// Instead, consider using package golang.org/x/crypto/chacha20poly1305.
|
||||||
|
func NewUnauthenticatedCipher(key, nonce []byte) (*Cipher, error) {
|
||||||
|
// This function is split into a wrapper so that the Cipher allocation will
|
||||||
|
// be inlined, and depending on how the caller uses the return value, won't
|
||||||
|
// escape to the heap.
|
||||||
|
c := &Cipher{}
|
||||||
|
return newUnauthenticatedCipher(c, key, nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUnauthenticatedCipher(c *Cipher, key, nonce []byte) (*Cipher, error) {
|
||||||
|
if len(key) != KeySize {
|
||||||
|
return nil, errors.New("chacha20: wrong key size")
|
||||||
|
}
|
||||||
|
if len(nonce) == NonceSizeX {
|
||||||
|
// XChaCha20 uses the ChaCha20 core to mix 16 bytes of the nonce into a
|
||||||
|
// derived key, allowing it to operate on a nonce of 24 bytes. See
|
||||||
|
// draft-irtf-cfrg-xchacha-01, Section 2.3.
|
||||||
|
key, _ = HChaCha20(key, nonce[0:16])
|
||||||
|
cNonce := make([]byte, NonceSize)
|
||||||
|
copy(cNonce[4:12], nonce[16:24])
|
||||||
|
nonce = cNonce
|
||||||
|
} else if len(nonce) != NonceSize {
|
||||||
|
return nil, errors.New("chacha20: wrong nonce size")
|
||||||
|
}
|
||||||
|
|
||||||
|
key, nonce = key[:KeySize], nonce[:NonceSize] // bounds check elimination hint
|
||||||
|
c.key = [8]uint32{
|
||||||
|
binary.LittleEndian.Uint32(key[0:4]),
|
||||||
|
binary.LittleEndian.Uint32(key[4:8]),
|
||||||
|
binary.LittleEndian.Uint32(key[8:12]),
|
||||||
|
binary.LittleEndian.Uint32(key[12:16]),
|
||||||
|
binary.LittleEndian.Uint32(key[16:20]),
|
||||||
|
binary.LittleEndian.Uint32(key[20:24]),
|
||||||
|
binary.LittleEndian.Uint32(key[24:28]),
|
||||||
|
binary.LittleEndian.Uint32(key[28:32]),
|
||||||
|
}
|
||||||
|
c.nonce = [3]uint32{
|
||||||
|
binary.LittleEndian.Uint32(nonce[0:4]),
|
||||||
|
binary.LittleEndian.Uint32(nonce[4:8]),
|
||||||
|
binary.LittleEndian.Uint32(nonce[8:12]),
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The constant first 4 words of the ChaCha20 state.
|
||||||
|
const (
|
||||||
|
j0 uint32 = 0x61707865 // expa
|
||||||
|
j1 uint32 = 0x3320646e // nd 3
|
||||||
|
j2 uint32 = 0x79622d32 // 2-by
|
||||||
|
j3 uint32 = 0x6b206574 // te k
|
||||||
|
)
|
||||||
|
|
||||||
|
const blockSize = 64
|
||||||
|
|
||||||
|
// quarterRound is the core of ChaCha20. It shuffles the bits of 4 state words.
|
||||||
|
// It's executed 4 times for each of the 20 ChaCha20 rounds, operating on all 16
|
||||||
|
// words each round, in columnar or diagonal groups of 4 at a time.
|
||||||
|
func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) {
|
||||||
|
a += b
|
||||||
|
d ^= a
|
||||||
|
d = bits.RotateLeft32(d, 16)
|
||||||
|
c += d
|
||||||
|
b ^= c
|
||||||
|
b = bits.RotateLeft32(b, 12)
|
||||||
|
a += b
|
||||||
|
d ^= a
|
||||||
|
d = bits.RotateLeft32(d, 8)
|
||||||
|
c += d
|
||||||
|
b ^= c
|
||||||
|
b = bits.RotateLeft32(b, 7)
|
||||||
|
return a, b, c, d
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will
|
||||||
|
// behave as if (64 * counter) bytes had been encrypted so far.
|
||||||
|
//
|
||||||
|
// To prevent accidental counter reuse, SetCounter panics if counter is less
|
||||||
|
// than the current value.
|
||||||
|
//
|
||||||
|
// Note that the execution time of XORKeyStream is not independent of the
|
||||||
|
// counter value.
|
||||||
|
func (s *Cipher) SetCounter(counter uint32) {
|
||||||
|
// Internally, s may buffer multiple blocks, which complicates this
|
||||||
|
// implementation slightly. When checking whether the counter has rolled
|
||||||
|
// back, we must use both s.counter and s.len to determine how many blocks
|
||||||
|
// we have already output.
|
||||||
|
outputCounter := s.counter - uint32(s.len)/blockSize
|
||||||
|
if s.overflow || counter < outputCounter {
|
||||||
|
panic("chacha20: SetCounter attempted to rollback counter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the general case, we set the new counter value and reset s.len to 0,
|
||||||
|
// causing the next call to XORKeyStream to refill the buffer. However, if
|
||||||
|
// we're advancing within the existing buffer, we can save work by simply
|
||||||
|
// setting s.len.
|
||||||
|
if counter < s.counter {
|
||||||
|
s.len = int(s.counter-counter) * blockSize
|
||||||
|
} else {
|
||||||
|
s.counter = counter
|
||||||
|
s.len = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XORKeyStream XORs each byte in the given slice with a byte from the
|
||||||
|
// cipher's key stream. Dst and src must overlap entirely or not at all.
|
||||||
|
//
|
||||||
|
// If len(dst) < len(src), XORKeyStream will panic. It is acceptable
|
||||||
|
// to pass a dst bigger than src, and in that case, XORKeyStream will
|
||||||
|
// only update dst[:len(src)] and will not touch the rest of dst.
|
||||||
|
//
|
||||||
|
// Multiple calls to XORKeyStream behave as if the concatenation of
|
||||||
|
// the src buffers was passed in a single run. That is, Cipher
|
||||||
|
// maintains state and does not reset at each XORKeyStream call.
|
||||||
|
func (s *Cipher) XORKeyStream(dst, src []byte) {
|
||||||
|
if len(src) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(dst) < len(src) {
|
||||||
|
panic("chacha20: output smaller than input")
|
||||||
|
}
|
||||||
|
dst = dst[:len(src)]
|
||||||
|
if subtle.InexactOverlap(dst, src) {
|
||||||
|
panic("chacha20: invalid buffer overlap")
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, drain any remaining key stream from a previous XORKeyStream.
|
||||||
|
if s.len != 0 {
|
||||||
|
keyStream := s.buf[bufSize-s.len:]
|
||||||
|
if len(src) < len(keyStream) {
|
||||||
|
keyStream = keyStream[:len(src)]
|
||||||
|
}
|
||||||
|
_ = src[len(keyStream)-1] // bounds check elimination hint
|
||||||
|
for i, b := range keyStream {
|
||||||
|
dst[i] = src[i] ^ b
|
||||||
|
}
|
||||||
|
s.len -= len(keyStream)
|
||||||
|
dst, src = dst[len(keyStream):], src[len(keyStream):]
|
||||||
|
}
|
||||||
|
if len(src) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we'd need to let the counter overflow and keep generating output,
|
||||||
|
// panic immediately. If instead we'd only reach the last block, remember
|
||||||
|
// not to generate any more output after the buffer is drained.
|
||||||
|
numBlocks := (uint64(len(src)) + blockSize - 1) / blockSize
|
||||||
|
if s.overflow || uint64(s.counter)+numBlocks > 1<<32 {
|
||||||
|
panic("chacha20: counter overflow")
|
||||||
|
} else if uint64(s.counter)+numBlocks == 1<<32 {
|
||||||
|
s.overflow = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// xorKeyStreamBlocks implementations expect input lengths that are a
|
||||||
|
// multiple of bufSize. Platform-specific ones process multiple blocks at a
|
||||||
|
// time, so have bufSizes that are a multiple of blockSize.
|
||||||
|
|
||||||
|
full := len(src) - len(src)%bufSize
|
||||||
|
if full > 0 {
|
||||||
|
s.xorKeyStreamBlocks(dst[:full], src[:full])
|
||||||
|
}
|
||||||
|
dst, src = dst[full:], src[full:]
|
||||||
|
|
||||||
|
// If using a multi-block xorKeyStreamBlocks would overflow, use the generic
|
||||||
|
// one that does one block at a time.
|
||||||
|
const blocksPerBuf = bufSize / blockSize
|
||||||
|
if uint64(s.counter)+blocksPerBuf > 1<<32 {
|
||||||
|
s.buf = [bufSize]byte{}
|
||||||
|
numBlocks := (len(src) + blockSize - 1) / blockSize
|
||||||
|
buf := s.buf[bufSize-numBlocks*blockSize:]
|
||||||
|
copy(buf, src)
|
||||||
|
s.xorKeyStreamBlocksGeneric(buf, buf)
|
||||||
|
s.len = len(buf) - copy(dst, buf)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and
|
||||||
|
// keep the leftover keystream for the next XORKeyStream invocation.
|
||||||
|
if len(src) > 0 {
|
||||||
|
s.buf = [bufSize]byte{}
|
||||||
|
copy(s.buf[:], src)
|
||||||
|
s.xorKeyStreamBlocks(s.buf[:], s.buf[:])
|
||||||
|
s.len = bufSize - copy(dst, s.buf[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) {
|
||||||
|
if len(dst) != len(src) || len(dst)%blockSize != 0 {
|
||||||
|
panic("chacha20: internal error: wrong dst and/or src length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// To generate each block of key stream, the initial cipher state
|
||||||
|
// (represented below) is passed through 20 rounds of shuffling,
|
||||||
|
// alternatively applying quarterRounds by columns (like 1, 5, 9, 13)
|
||||||
|
// or by diagonals (like 1, 6, 11, 12).
|
||||||
|
//
|
||||||
|
// 0:cccccccc 1:cccccccc 2:cccccccc 3:cccccccc
|
||||||
|
// 4:kkkkkkkk 5:kkkkkkkk 6:kkkkkkkk 7:kkkkkkkk
|
||||||
|
// 8:kkkkkkkk 9:kkkkkkkk 10:kkkkkkkk 11:kkkkkkkk
|
||||||
|
// 12:bbbbbbbb 13:nnnnnnnn 14:nnnnnnnn 15:nnnnnnnn
|
||||||
|
//
|
||||||
|
// c=constant k=key b=blockcount n=nonce
|
||||||
|
var (
|
||||||
|
c0, c1, c2, c3 = j0, j1, j2, j3
|
||||||
|
c4, c5, c6, c7 = s.key[0], s.key[1], s.key[2], s.key[3]
|
||||||
|
c8, c9, c10, c11 = s.key[4], s.key[5], s.key[6], s.key[7]
|
||||||
|
_, c13, c14, c15 = s.counter, s.nonce[0], s.nonce[1], s.nonce[2]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Three quarters of the first round don't depend on the counter, so we can
|
||||||
|
// calculate them here, and reuse them for multiple blocks in the loop, and
|
||||||
|
// for future XORKeyStream invocations.
|
||||||
|
if !s.precompDone {
|
||||||
|
s.p1, s.p5, s.p9, s.p13 = quarterRound(c1, c5, c9, c13)
|
||||||
|
s.p2, s.p6, s.p10, s.p14 = quarterRound(c2, c6, c10, c14)
|
||||||
|
s.p3, s.p7, s.p11, s.p15 = quarterRound(c3, c7, c11, c15)
|
||||||
|
s.precompDone = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// A condition of len(src) > 0 would be sufficient, but this also
|
||||||
|
// acts as a bounds check elimination hint.
|
||||||
|
for len(src) >= 64 && len(dst) >= 64 {
|
||||||
|
// The remainder of the first column round.
|
||||||
|
fcr0, fcr4, fcr8, fcr12 := quarterRound(c0, c4, c8, s.counter)
|
||||||
|
|
||||||
|
// The second diagonal round.
|
||||||
|
x0, x5, x10, x15 := quarterRound(fcr0, s.p5, s.p10, s.p15)
|
||||||
|
x1, x6, x11, x12 := quarterRound(s.p1, s.p6, s.p11, fcr12)
|
||||||
|
x2, x7, x8, x13 := quarterRound(s.p2, s.p7, fcr8, s.p13)
|
||||||
|
x3, x4, x9, x14 := quarterRound(s.p3, fcr4, s.p9, s.p14)
|
||||||
|
|
||||||
|
// The remaining 18 rounds.
|
||||||
|
for i := 0; i < 9; i++ {
|
||||||
|
// Column round.
|
||||||
|
x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
|
||||||
|
x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
|
||||||
|
x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
|
||||||
|
x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
|
||||||
|
|
||||||
|
// Diagonal round.
|
||||||
|
x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
|
||||||
|
x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
|
||||||
|
x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
|
||||||
|
x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add back the initial state to generate the key stream, then
|
||||||
|
// XOR the key stream with the source and write out the result.
|
||||||
|
addXor(dst[0:4], src[0:4], x0, c0)
|
||||||
|
addXor(dst[4:8], src[4:8], x1, c1)
|
||||||
|
addXor(dst[8:12], src[8:12], x2, c2)
|
||||||
|
addXor(dst[12:16], src[12:16], x3, c3)
|
||||||
|
addXor(dst[16:20], src[16:20], x4, c4)
|
||||||
|
addXor(dst[20:24], src[20:24], x5, c5)
|
||||||
|
addXor(dst[24:28], src[24:28], x6, c6)
|
||||||
|
addXor(dst[28:32], src[28:32], x7, c7)
|
||||||
|
addXor(dst[32:36], src[32:36], x8, c8)
|
||||||
|
addXor(dst[36:40], src[36:40], x9, c9)
|
||||||
|
addXor(dst[40:44], src[40:44], x10, c10)
|
||||||
|
addXor(dst[44:48], src[44:48], x11, c11)
|
||||||
|
addXor(dst[48:52], src[48:52], x12, s.counter)
|
||||||
|
addXor(dst[52:56], src[52:56], x13, c13)
|
||||||
|
addXor(dst[56:60], src[56:60], x14, c14)
|
||||||
|
addXor(dst[60:64], src[60:64], x15, c15)
|
||||||
|
|
||||||
|
s.counter += 1
|
||||||
|
|
||||||
|
src, dst = src[blockSize:], dst[blockSize:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HChaCha20 uses the ChaCha20 core to generate a derived key from a 32 bytes
|
||||||
|
// key and a 16 bytes nonce. It returns an error if key or nonce have any other
|
||||||
|
// length. It is used as part of the XChaCha20 construction.
|
||||||
|
func HChaCha20(key, nonce []byte) ([]byte, error) {
|
||||||
|
// This function is split into a wrapper so that the slice allocation will
|
||||||
|
// be inlined, and depending on how the caller uses the return value, won't
|
||||||
|
// escape to the heap.
|
||||||
|
out := make([]byte, 32)
|
||||||
|
return hChaCha20(out, key, nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hChaCha20(out, key, nonce []byte) ([]byte, error) {
|
||||||
|
if len(key) != KeySize {
|
||||||
|
return nil, errors.New("chacha20: wrong HChaCha20 key size")
|
||||||
|
}
|
||||||
|
if len(nonce) != 16 {
|
||||||
|
return nil, errors.New("chacha20: wrong HChaCha20 nonce size")
|
||||||
|
}
|
||||||
|
|
||||||
|
x0, x1, x2, x3 := j0, j1, j2, j3
|
||||||
|
x4 := binary.LittleEndian.Uint32(key[0:4])
|
||||||
|
x5 := binary.LittleEndian.Uint32(key[4:8])
|
||||||
|
x6 := binary.LittleEndian.Uint32(key[8:12])
|
||||||
|
x7 := binary.LittleEndian.Uint32(key[12:16])
|
||||||
|
x8 := binary.LittleEndian.Uint32(key[16:20])
|
||||||
|
x9 := binary.LittleEndian.Uint32(key[20:24])
|
||||||
|
x10 := binary.LittleEndian.Uint32(key[24:28])
|
||||||
|
x11 := binary.LittleEndian.Uint32(key[28:32])
|
||||||
|
x12 := binary.LittleEndian.Uint32(nonce[0:4])
|
||||||
|
x13 := binary.LittleEndian.Uint32(nonce[4:8])
|
||||||
|
x14 := binary.LittleEndian.Uint32(nonce[8:12])
|
||||||
|
x15 := binary.LittleEndian.Uint32(nonce[12:16])
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
// Diagonal round.
|
||||||
|
x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12)
|
||||||
|
x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13)
|
||||||
|
x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14)
|
||||||
|
x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15)
|
||||||
|
|
||||||
|
// Column round.
|
||||||
|
x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15)
|
||||||
|
x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12)
|
||||||
|
x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13)
|
||||||
|
x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = out[31] // bounds check elimination hint
|
||||||
|
binary.LittleEndian.PutUint32(out[0:4], x0)
|
||||||
|
binary.LittleEndian.PutUint32(out[4:8], x1)
|
||||||
|
binary.LittleEndian.PutUint32(out[8:12], x2)
|
||||||
|
binary.LittleEndian.PutUint32(out[12:16], x3)
|
||||||
|
binary.LittleEndian.PutUint32(out[16:20], x12)
|
||||||
|
binary.LittleEndian.PutUint32(out[20:24], x13)
|
||||||
|
binary.LittleEndian.PutUint32(out[24:28], x14)
|
||||||
|
binary.LittleEndian.PutUint32(out[28:32], x15)
|
||||||
|
return out, nil
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2018 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 (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego
|
||||||
|
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
|
||||||
|
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
const bufSize = blockSize
|
||||||
|
|
||||||
|
func (s *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||||
|
s.xorKeyStreamBlocksGeneric(dst, src)
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gc && !purego
|
||||||
|
// +build gc,!purego
|
||||||
|
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
const bufSize = 256
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||||
|
|
||||||
|
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||||
|
chaCha20_ctr32_vsx(&dst[0], &src[0], len(src), &c.key, &c.counter)
|
||||||
|
}
|
|
@ -0,0 +1,449 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Based on CRYPTOGAMS code with the following comment:
|
||||||
|
// # ====================================================================
|
||||||
|
// # Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
|
||||||
|
// # project. The module is, however, dual licensed under OpenSSL and
|
||||||
|
// # CRYPTOGAMS licenses depending on where you obtain it. For further
|
||||||
|
// # details see http://www.openssl.org/~appro/cryptogams/.
|
||||||
|
// # ====================================================================
|
||||||
|
|
||||||
|
// Code for the perl script that generates the ppc64 assembler
|
||||||
|
// can be found in the cryptogams repository at the link below. It is based on
|
||||||
|
// the original from openssl.
|
||||||
|
|
||||||
|
// https://github.com/dot-asm/cryptogams/commit/a60f5b50ed908e91
|
||||||
|
|
||||||
|
// The differences in this and the original implementation are
|
||||||
|
// due to the calling conventions and initialization of constants.
|
||||||
|
|
||||||
|
// +build gc,!purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
#define OUT R3
|
||||||
|
#define INP R4
|
||||||
|
#define LEN R5
|
||||||
|
#define KEY R6
|
||||||
|
#define CNT R7
|
||||||
|
#define TMP R15
|
||||||
|
|
||||||
|
#define CONSTBASE R16
|
||||||
|
#define BLOCKS R17
|
||||||
|
|
||||||
|
DATA consts<>+0x00(SB)/8, $0x3320646e61707865
|
||||||
|
DATA consts<>+0x08(SB)/8, $0x6b20657479622d32
|
||||||
|
DATA consts<>+0x10(SB)/8, $0x0000000000000001
|
||||||
|
DATA consts<>+0x18(SB)/8, $0x0000000000000000
|
||||||
|
DATA consts<>+0x20(SB)/8, $0x0000000000000004
|
||||||
|
DATA consts<>+0x28(SB)/8, $0x0000000000000000
|
||||||
|
DATA consts<>+0x30(SB)/8, $0x0a0b08090e0f0c0d
|
||||||
|
DATA consts<>+0x38(SB)/8, $0x0203000106070405
|
||||||
|
DATA consts<>+0x40(SB)/8, $0x090a0b080d0e0f0c
|
||||||
|
DATA consts<>+0x48(SB)/8, $0x0102030005060704
|
||||||
|
DATA consts<>+0x50(SB)/8, $0x6170786561707865
|
||||||
|
DATA consts<>+0x58(SB)/8, $0x6170786561707865
|
||||||
|
DATA consts<>+0x60(SB)/8, $0x3320646e3320646e
|
||||||
|
DATA consts<>+0x68(SB)/8, $0x3320646e3320646e
|
||||||
|
DATA consts<>+0x70(SB)/8, $0x79622d3279622d32
|
||||||
|
DATA consts<>+0x78(SB)/8, $0x79622d3279622d32
|
||||||
|
DATA consts<>+0x80(SB)/8, $0x6b2065746b206574
|
||||||
|
DATA consts<>+0x88(SB)/8, $0x6b2065746b206574
|
||||||
|
DATA consts<>+0x90(SB)/8, $0x0000000100000000
|
||||||
|
DATA consts<>+0x98(SB)/8, $0x0000000300000002
|
||||||
|
GLOBL consts<>(SB), RODATA, $0xa0
|
||||||
|
|
||||||
|
//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||||
|
TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||||
|
MOVD out+0(FP), OUT
|
||||||
|
MOVD inp+8(FP), INP
|
||||||
|
MOVD len+16(FP), LEN
|
||||||
|
MOVD key+24(FP), KEY
|
||||||
|
MOVD counter+32(FP), CNT
|
||||||
|
|
||||||
|
// Addressing for constants
|
||||||
|
MOVD $consts<>+0x00(SB), CONSTBASE
|
||||||
|
MOVD $16, R8
|
||||||
|
MOVD $32, R9
|
||||||
|
MOVD $48, R10
|
||||||
|
MOVD $64, R11
|
||||||
|
SRD $6, LEN, BLOCKS
|
||||||
|
// V16
|
||||||
|
LXVW4X (CONSTBASE)(R0), VS48
|
||||||
|
ADD $80,CONSTBASE
|
||||||
|
|
||||||
|
// Load key into V17,V18
|
||||||
|
LXVW4X (KEY)(R0), VS49
|
||||||
|
LXVW4X (KEY)(R8), VS50
|
||||||
|
|
||||||
|
// Load CNT, NONCE into V19
|
||||||
|
LXVW4X (CNT)(R0), VS51
|
||||||
|
|
||||||
|
// Clear V27
|
||||||
|
VXOR V27, V27, V27
|
||||||
|
|
||||||
|
// V28
|
||||||
|
LXVW4X (CONSTBASE)(R11), VS60
|
||||||
|
|
||||||
|
// splat slot from V19 -> V26
|
||||||
|
VSPLTW $0, V19, V26
|
||||||
|
|
||||||
|
VSLDOI $4, V19, V27, V19
|
||||||
|
VSLDOI $12, V27, V19, V19
|
||||||
|
|
||||||
|
VADDUWM V26, V28, V26
|
||||||
|
|
||||||
|
MOVD $10, R14
|
||||||
|
MOVD R14, CTR
|
||||||
|
|
||||||
|
loop_outer_vsx:
|
||||||
|
// V0, V1, V2, V3
|
||||||
|
LXVW4X (R0)(CONSTBASE), VS32
|
||||||
|
LXVW4X (R8)(CONSTBASE), VS33
|
||||||
|
LXVW4X (R9)(CONSTBASE), VS34
|
||||||
|
LXVW4X (R10)(CONSTBASE), VS35
|
||||||
|
|
||||||
|
// splat values from V17, V18 into V4-V11
|
||||||
|
VSPLTW $0, V17, V4
|
||||||
|
VSPLTW $1, V17, V5
|
||||||
|
VSPLTW $2, V17, V6
|
||||||
|
VSPLTW $3, V17, V7
|
||||||
|
VSPLTW $0, V18, V8
|
||||||
|
VSPLTW $1, V18, V9
|
||||||
|
VSPLTW $2, V18, V10
|
||||||
|
VSPLTW $3, V18, V11
|
||||||
|
|
||||||
|
// VOR
|
||||||
|
VOR V26, V26, V12
|
||||||
|
|
||||||
|
// splat values from V19 -> V13, V14, V15
|
||||||
|
VSPLTW $1, V19, V13
|
||||||
|
VSPLTW $2, V19, V14
|
||||||
|
VSPLTW $3, V19, V15
|
||||||
|
|
||||||
|
// splat const values
|
||||||
|
VSPLTISW $-16, V27
|
||||||
|
VSPLTISW $12, V28
|
||||||
|
VSPLTISW $8, V29
|
||||||
|
VSPLTISW $7, V30
|
||||||
|
|
||||||
|
loop_vsx:
|
||||||
|
VADDUWM V0, V4, V0
|
||||||
|
VADDUWM V1, V5, V1
|
||||||
|
VADDUWM V2, V6, V2
|
||||||
|
VADDUWM V3, V7, V3
|
||||||
|
|
||||||
|
VXOR V12, V0, V12
|
||||||
|
VXOR V13, V1, V13
|
||||||
|
VXOR V14, V2, V14
|
||||||
|
VXOR V15, V3, V15
|
||||||
|
|
||||||
|
VRLW V12, V27, V12
|
||||||
|
VRLW V13, V27, V13
|
||||||
|
VRLW V14, V27, V14
|
||||||
|
VRLW V15, V27, V15
|
||||||
|
|
||||||
|
VADDUWM V8, V12, V8
|
||||||
|
VADDUWM V9, V13, V9
|
||||||
|
VADDUWM V10, V14, V10
|
||||||
|
VADDUWM V11, V15, V11
|
||||||
|
|
||||||
|
VXOR V4, V8, V4
|
||||||
|
VXOR V5, V9, V5
|
||||||
|
VXOR V6, V10, V6
|
||||||
|
VXOR V7, V11, V7
|
||||||
|
|
||||||
|
VRLW V4, V28, V4
|
||||||
|
VRLW V5, V28, V5
|
||||||
|
VRLW V6, V28, V6
|
||||||
|
VRLW V7, V28, V7
|
||||||
|
|
||||||
|
VADDUWM V0, V4, V0
|
||||||
|
VADDUWM V1, V5, V1
|
||||||
|
VADDUWM V2, V6, V2
|
||||||
|
VADDUWM V3, V7, V3
|
||||||
|
|
||||||
|
VXOR V12, V0, V12
|
||||||
|
VXOR V13, V1, V13
|
||||||
|
VXOR V14, V2, V14
|
||||||
|
VXOR V15, V3, V15
|
||||||
|
|
||||||
|
VRLW V12, V29, V12
|
||||||
|
VRLW V13, V29, V13
|
||||||
|
VRLW V14, V29, V14
|
||||||
|
VRLW V15, V29, V15
|
||||||
|
|
||||||
|
VADDUWM V8, V12, V8
|
||||||
|
VADDUWM V9, V13, V9
|
||||||
|
VADDUWM V10, V14, V10
|
||||||
|
VADDUWM V11, V15, V11
|
||||||
|
|
||||||
|
VXOR V4, V8, V4
|
||||||
|
VXOR V5, V9, V5
|
||||||
|
VXOR V6, V10, V6
|
||||||
|
VXOR V7, V11, V7
|
||||||
|
|
||||||
|
VRLW V4, V30, V4
|
||||||
|
VRLW V5, V30, V5
|
||||||
|
VRLW V6, V30, V6
|
||||||
|
VRLW V7, V30, V7
|
||||||
|
|
||||||
|
VADDUWM V0, V5, V0
|
||||||
|
VADDUWM V1, V6, V1
|
||||||
|
VADDUWM V2, V7, V2
|
||||||
|
VADDUWM V3, V4, V3
|
||||||
|
|
||||||
|
VXOR V15, V0, V15
|
||||||
|
VXOR V12, V1, V12
|
||||||
|
VXOR V13, V2, V13
|
||||||
|
VXOR V14, V3, V14
|
||||||
|
|
||||||
|
VRLW V15, V27, V15
|
||||||
|
VRLW V12, V27, V12
|
||||||
|
VRLW V13, V27, V13
|
||||||
|
VRLW V14, V27, V14
|
||||||
|
|
||||||
|
VADDUWM V10, V15, V10
|
||||||
|
VADDUWM V11, V12, V11
|
||||||
|
VADDUWM V8, V13, V8
|
||||||
|
VADDUWM V9, V14, V9
|
||||||
|
|
||||||
|
VXOR V5, V10, V5
|
||||||
|
VXOR V6, V11, V6
|
||||||
|
VXOR V7, V8, V7
|
||||||
|
VXOR V4, V9, V4
|
||||||
|
|
||||||
|
VRLW V5, V28, V5
|
||||||
|
VRLW V6, V28, V6
|
||||||
|
VRLW V7, V28, V7
|
||||||
|
VRLW V4, V28, V4
|
||||||
|
|
||||||
|
VADDUWM V0, V5, V0
|
||||||
|
VADDUWM V1, V6, V1
|
||||||
|
VADDUWM V2, V7, V2
|
||||||
|
VADDUWM V3, V4, V3
|
||||||
|
|
||||||
|
VXOR V15, V0, V15
|
||||||
|
VXOR V12, V1, V12
|
||||||
|
VXOR V13, V2, V13
|
||||||
|
VXOR V14, V3, V14
|
||||||
|
|
||||||
|
VRLW V15, V29, V15
|
||||||
|
VRLW V12, V29, V12
|
||||||
|
VRLW V13, V29, V13
|
||||||
|
VRLW V14, V29, V14
|
||||||
|
|
||||||
|
VADDUWM V10, V15, V10
|
||||||
|
VADDUWM V11, V12, V11
|
||||||
|
VADDUWM V8, V13, V8
|
||||||
|
VADDUWM V9, V14, V9
|
||||||
|
|
||||||
|
VXOR V5, V10, V5
|
||||||
|
VXOR V6, V11, V6
|
||||||
|
VXOR V7, V8, V7
|
||||||
|
VXOR V4, V9, V4
|
||||||
|
|
||||||
|
VRLW V5, V30, V5
|
||||||
|
VRLW V6, V30, V6
|
||||||
|
VRLW V7, V30, V7
|
||||||
|
VRLW V4, V30, V4
|
||||||
|
BC 16, LT, loop_vsx
|
||||||
|
|
||||||
|
VADDUWM V12, V26, V12
|
||||||
|
|
||||||
|
WORD $0x13600F8C // VMRGEW V0, V1, V27
|
||||||
|
WORD $0x13821F8C // VMRGEW V2, V3, V28
|
||||||
|
|
||||||
|
WORD $0x10000E8C // VMRGOW V0, V1, V0
|
||||||
|
WORD $0x10421E8C // VMRGOW V2, V3, V2
|
||||||
|
|
||||||
|
WORD $0x13A42F8C // VMRGEW V4, V5, V29
|
||||||
|
WORD $0x13C63F8C // VMRGEW V6, V7, V30
|
||||||
|
|
||||||
|
XXPERMDI VS32, VS34, $0, VS33
|
||||||
|
XXPERMDI VS32, VS34, $3, VS35
|
||||||
|
XXPERMDI VS59, VS60, $0, VS32
|
||||||
|
XXPERMDI VS59, VS60, $3, VS34
|
||||||
|
|
||||||
|
WORD $0x10842E8C // VMRGOW V4, V5, V4
|
||||||
|
WORD $0x10C63E8C // VMRGOW V6, V7, V6
|
||||||
|
|
||||||
|
WORD $0x13684F8C // VMRGEW V8, V9, V27
|
||||||
|
WORD $0x138A5F8C // VMRGEW V10, V11, V28
|
||||||
|
|
||||||
|
XXPERMDI VS36, VS38, $0, VS37
|
||||||
|
XXPERMDI VS36, VS38, $3, VS39
|
||||||
|
XXPERMDI VS61, VS62, $0, VS36
|
||||||
|
XXPERMDI VS61, VS62, $3, VS38
|
||||||
|
|
||||||
|
WORD $0x11084E8C // VMRGOW V8, V9, V8
|
||||||
|
WORD $0x114A5E8C // VMRGOW V10, V11, V10
|
||||||
|
|
||||||
|
WORD $0x13AC6F8C // VMRGEW V12, V13, V29
|
||||||
|
WORD $0x13CE7F8C // VMRGEW V14, V15, V30
|
||||||
|
|
||||||
|
XXPERMDI VS40, VS42, $0, VS41
|
||||||
|
XXPERMDI VS40, VS42, $3, VS43
|
||||||
|
XXPERMDI VS59, VS60, $0, VS40
|
||||||
|
XXPERMDI VS59, VS60, $3, VS42
|
||||||
|
|
||||||
|
WORD $0x118C6E8C // VMRGOW V12, V13, V12
|
||||||
|
WORD $0x11CE7E8C // VMRGOW V14, V15, V14
|
||||||
|
|
||||||
|
VSPLTISW $4, V27
|
||||||
|
VADDUWM V26, V27, V26
|
||||||
|
|
||||||
|
XXPERMDI VS44, VS46, $0, VS45
|
||||||
|
XXPERMDI VS44, VS46, $3, VS47
|
||||||
|
XXPERMDI VS61, VS62, $0, VS44
|
||||||
|
XXPERMDI VS61, VS62, $3, VS46
|
||||||
|
|
||||||
|
VADDUWM V0, V16, V0
|
||||||
|
VADDUWM V4, V17, V4
|
||||||
|
VADDUWM V8, V18, V8
|
||||||
|
VADDUWM V12, V19, V12
|
||||||
|
|
||||||
|
CMPU LEN, $64
|
||||||
|
BLT tail_vsx
|
||||||
|
|
||||||
|
// Bottom of loop
|
||||||
|
LXVW4X (INP)(R0), VS59
|
||||||
|
LXVW4X (INP)(R8), VS60
|
||||||
|
LXVW4X (INP)(R9), VS61
|
||||||
|
LXVW4X (INP)(R10), VS62
|
||||||
|
|
||||||
|
VXOR V27, V0, V27
|
||||||
|
VXOR V28, V4, V28
|
||||||
|
VXOR V29, V8, V29
|
||||||
|
VXOR V30, V12, V30
|
||||||
|
|
||||||
|
STXVW4X VS59, (OUT)(R0)
|
||||||
|
STXVW4X VS60, (OUT)(R8)
|
||||||
|
ADD $64, INP
|
||||||
|
STXVW4X VS61, (OUT)(R9)
|
||||||
|
ADD $-64, LEN
|
||||||
|
STXVW4X VS62, (OUT)(R10)
|
||||||
|
ADD $64, OUT
|
||||||
|
BEQ done_vsx
|
||||||
|
|
||||||
|
VADDUWM V1, V16, V0
|
||||||
|
VADDUWM V5, V17, V4
|
||||||
|
VADDUWM V9, V18, V8
|
||||||
|
VADDUWM V13, V19, V12
|
||||||
|
|
||||||
|
CMPU LEN, $64
|
||||||
|
BLT tail_vsx
|
||||||
|
|
||||||
|
LXVW4X (INP)(R0), VS59
|
||||||
|
LXVW4X (INP)(R8), VS60
|
||||||
|
LXVW4X (INP)(R9), VS61
|
||||||
|
LXVW4X (INP)(R10), VS62
|
||||||
|
VXOR V27, V0, V27
|
||||||
|
|
||||||
|
VXOR V28, V4, V28
|
||||||
|
VXOR V29, V8, V29
|
||||||
|
VXOR V30, V12, V30
|
||||||
|
|
||||||
|
STXVW4X VS59, (OUT)(R0)
|
||||||
|
STXVW4X VS60, (OUT)(R8)
|
||||||
|
ADD $64, INP
|
||||||
|
STXVW4X VS61, (OUT)(R9)
|
||||||
|
ADD $-64, LEN
|
||||||
|
STXVW4X VS62, (OUT)(V10)
|
||||||
|
ADD $64, OUT
|
||||||
|
BEQ done_vsx
|
||||||
|
|
||||||
|
VADDUWM V2, V16, V0
|
||||||
|
VADDUWM V6, V17, V4
|
||||||
|
VADDUWM V10, V18, V8
|
||||||
|
VADDUWM V14, V19, V12
|
||||||
|
|
||||||
|
CMPU LEN, $64
|
||||||
|
BLT tail_vsx
|
||||||
|
|
||||||
|
LXVW4X (INP)(R0), VS59
|
||||||
|
LXVW4X (INP)(R8), VS60
|
||||||
|
LXVW4X (INP)(R9), VS61
|
||||||
|
LXVW4X (INP)(R10), VS62
|
||||||
|
|
||||||
|
VXOR V27, V0, V27
|
||||||
|
VXOR V28, V4, V28
|
||||||
|
VXOR V29, V8, V29
|
||||||
|
VXOR V30, V12, V30
|
||||||
|
|
||||||
|
STXVW4X VS59, (OUT)(R0)
|
||||||
|
STXVW4X VS60, (OUT)(R8)
|
||||||
|
ADD $64, INP
|
||||||
|
STXVW4X VS61, (OUT)(R9)
|
||||||
|
ADD $-64, LEN
|
||||||
|
STXVW4X VS62, (OUT)(R10)
|
||||||
|
ADD $64, OUT
|
||||||
|
BEQ done_vsx
|
||||||
|
|
||||||
|
VADDUWM V3, V16, V0
|
||||||
|
VADDUWM V7, V17, V4
|
||||||
|
VADDUWM V11, V18, V8
|
||||||
|
VADDUWM V15, V19, V12
|
||||||
|
|
||||||
|
CMPU LEN, $64
|
||||||
|
BLT tail_vsx
|
||||||
|
|
||||||
|
LXVW4X (INP)(R0), VS59
|
||||||
|
LXVW4X (INP)(R8), VS60
|
||||||
|
LXVW4X (INP)(R9), VS61
|
||||||
|
LXVW4X (INP)(R10), VS62
|
||||||
|
|
||||||
|
VXOR V27, V0, V27
|
||||||
|
VXOR V28, V4, V28
|
||||||
|
VXOR V29, V8, V29
|
||||||
|
VXOR V30, V12, V30
|
||||||
|
|
||||||
|
STXVW4X VS59, (OUT)(R0)
|
||||||
|
STXVW4X VS60, (OUT)(R8)
|
||||||
|
ADD $64, INP
|
||||||
|
STXVW4X VS61, (OUT)(R9)
|
||||||
|
ADD $-64, LEN
|
||||||
|
STXVW4X VS62, (OUT)(R10)
|
||||||
|
ADD $64, OUT
|
||||||
|
|
||||||
|
MOVD $10, R14
|
||||||
|
MOVD R14, CTR
|
||||||
|
BNE loop_outer_vsx
|
||||||
|
|
||||||
|
done_vsx:
|
||||||
|
// Increment counter by number of 64 byte blocks
|
||||||
|
MOVD (CNT), R14
|
||||||
|
ADD BLOCKS, R14
|
||||||
|
MOVD R14, (CNT)
|
||||||
|
RET
|
||||||
|
|
||||||
|
tail_vsx:
|
||||||
|
ADD $32, R1, R11
|
||||||
|
MOVD LEN, CTR
|
||||||
|
|
||||||
|
// Save values on stack to copy from
|
||||||
|
STXVW4X VS32, (R11)(R0)
|
||||||
|
STXVW4X VS36, (R11)(R8)
|
||||||
|
STXVW4X VS40, (R11)(R9)
|
||||||
|
STXVW4X VS44, (R11)(R10)
|
||||||
|
ADD $-1, R11, R12
|
||||||
|
ADD $-1, INP
|
||||||
|
ADD $-1, OUT
|
||||||
|
|
||||||
|
looptail_vsx:
|
||||||
|
// Copying the result to OUT
|
||||||
|
// in bytes.
|
||||||
|
MOVBZU 1(R12), KEY
|
||||||
|
MOVBZU 1(INP), TMP
|
||||||
|
XOR KEY, TMP, KEY
|
||||||
|
MOVBU KEY, 1(OUT)
|
||||||
|
BC 16, LT, looptail_vsx
|
||||||
|
|
||||||
|
// Clear the stack values
|
||||||
|
STXVW4X VS48, (R11)(R0)
|
||||||
|
STXVW4X VS48, (R11)(R8)
|
||||||
|
STXVW4X VS48, (R11)(R9)
|
||||||
|
STXVW4X VS48, (R11)(R10)
|
||||||
|
BR done_vsx
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2018 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
|
||||||
|
// +build gc,!purego
|
||||||
|
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
import "golang.org/x/sys/cpu"
|
||||||
|
|
||||||
|
var haveAsm = cpu.S390X.HasVX
|
||||||
|
|
||||||
|
const bufSize = 256
|
||||||
|
|
||||||
|
// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only
|
||||||
|
// be called when the vector facility is available. Implementation in asm_s390x.s.
|
||||||
|
//go:noescape
|
||||||
|
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||||
|
|
||||||
|
func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) {
|
||||||
|
if cpu.S390X.HasVX {
|
||||||
|
xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter)
|
||||||
|
} else {
|
||||||
|
c.xorKeyStreamBlocksGeneric(dst, src)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,224 @@
|
||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
// +build gc,!purego
|
||||||
|
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// This is an implementation of the ChaCha20 encryption algorithm as
|
||||||
|
// specified in RFC 7539. It uses vector instructions to compute
|
||||||
|
// 4 keystream blocks in parallel (256 bytes) which are then XORed
|
||||||
|
// with the bytes in the input slice.
|
||||||
|
|
||||||
|
GLOBL ·constants<>(SB), RODATA|NOPTR, $32
|
||||||
|
// BSWAP: swap bytes in each 4-byte element
|
||||||
|
DATA ·constants<>+0x00(SB)/4, $0x03020100
|
||||||
|
DATA ·constants<>+0x04(SB)/4, $0x07060504
|
||||||
|
DATA ·constants<>+0x08(SB)/4, $0x0b0a0908
|
||||||
|
DATA ·constants<>+0x0c(SB)/4, $0x0f0e0d0c
|
||||||
|
// J0: [j0, j1, j2, j3]
|
||||||
|
DATA ·constants<>+0x10(SB)/4, $0x61707865
|
||||||
|
DATA ·constants<>+0x14(SB)/4, $0x3320646e
|
||||||
|
DATA ·constants<>+0x18(SB)/4, $0x79622d32
|
||||||
|
DATA ·constants<>+0x1c(SB)/4, $0x6b206574
|
||||||
|
|
||||||
|
#define BSWAP V5
|
||||||
|
#define J0 V6
|
||||||
|
#define KEY0 V7
|
||||||
|
#define KEY1 V8
|
||||||
|
#define NONCE V9
|
||||||
|
#define CTR V10
|
||||||
|
#define M0 V11
|
||||||
|
#define M1 V12
|
||||||
|
#define M2 V13
|
||||||
|
#define M3 V14
|
||||||
|
#define INC V15
|
||||||
|
#define X0 V16
|
||||||
|
#define X1 V17
|
||||||
|
#define X2 V18
|
||||||
|
#define X3 V19
|
||||||
|
#define X4 V20
|
||||||
|
#define X5 V21
|
||||||
|
#define X6 V22
|
||||||
|
#define X7 V23
|
||||||
|
#define X8 V24
|
||||||
|
#define X9 V25
|
||||||
|
#define X10 V26
|
||||||
|
#define X11 V27
|
||||||
|
#define X12 V28
|
||||||
|
#define X13 V29
|
||||||
|
#define X14 V30
|
||||||
|
#define X15 V31
|
||||||
|
|
||||||
|
#define NUM_ROUNDS 20
|
||||||
|
|
||||||
|
#define ROUND4(a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3, d0, d1, d2, d3) \
|
||||||
|
VAF a1, a0, a0 \
|
||||||
|
VAF b1, b0, b0 \
|
||||||
|
VAF c1, c0, c0 \
|
||||||
|
VAF d1, d0, d0 \
|
||||||
|
VX a0, a2, a2 \
|
||||||
|
VX b0, b2, b2 \
|
||||||
|
VX c0, c2, c2 \
|
||||||
|
VX d0, d2, d2 \
|
||||||
|
VERLLF $16, a2, a2 \
|
||||||
|
VERLLF $16, b2, b2 \
|
||||||
|
VERLLF $16, c2, c2 \
|
||||||
|
VERLLF $16, d2, d2 \
|
||||||
|
VAF a2, a3, a3 \
|
||||||
|
VAF b2, b3, b3 \
|
||||||
|
VAF c2, c3, c3 \
|
||||||
|
VAF d2, d3, d3 \
|
||||||
|
VX a3, a1, a1 \
|
||||||
|
VX b3, b1, b1 \
|
||||||
|
VX c3, c1, c1 \
|
||||||
|
VX d3, d1, d1 \
|
||||||
|
VERLLF $12, a1, a1 \
|
||||||
|
VERLLF $12, b1, b1 \
|
||||||
|
VERLLF $12, c1, c1 \
|
||||||
|
VERLLF $12, d1, d1 \
|
||||||
|
VAF a1, a0, a0 \
|
||||||
|
VAF b1, b0, b0 \
|
||||||
|
VAF c1, c0, c0 \
|
||||||
|
VAF d1, d0, d0 \
|
||||||
|
VX a0, a2, a2 \
|
||||||
|
VX b0, b2, b2 \
|
||||||
|
VX c0, c2, c2 \
|
||||||
|
VX d0, d2, d2 \
|
||||||
|
VERLLF $8, a2, a2 \
|
||||||
|
VERLLF $8, b2, b2 \
|
||||||
|
VERLLF $8, c2, c2 \
|
||||||
|
VERLLF $8, d2, d2 \
|
||||||
|
VAF a2, a3, a3 \
|
||||||
|
VAF b2, b3, b3 \
|
||||||
|
VAF c2, c3, c3 \
|
||||||
|
VAF d2, d3, d3 \
|
||||||
|
VX a3, a1, a1 \
|
||||||
|
VX b3, b1, b1 \
|
||||||
|
VX c3, c1, c1 \
|
||||||
|
VX d3, d1, d1 \
|
||||||
|
VERLLF $7, a1, a1 \
|
||||||
|
VERLLF $7, b1, b1 \
|
||||||
|
VERLLF $7, c1, c1 \
|
||||||
|
VERLLF $7, d1, d1
|
||||||
|
|
||||||
|
#define PERMUTE(mask, v0, v1, v2, v3) \
|
||||||
|
VPERM v0, v0, mask, v0 \
|
||||||
|
VPERM v1, v1, mask, v1 \
|
||||||
|
VPERM v2, v2, mask, v2 \
|
||||||
|
VPERM v3, v3, mask, v3
|
||||||
|
|
||||||
|
#define ADDV(x, v0, v1, v2, v3) \
|
||||||
|
VAF x, v0, v0 \
|
||||||
|
VAF x, v1, v1 \
|
||||||
|
VAF x, v2, v2 \
|
||||||
|
VAF x, v3, v3
|
||||||
|
|
||||||
|
#define XORV(off, dst, src, v0, v1, v2, v3) \
|
||||||
|
VLM off(src), M0, M3 \
|
||||||
|
PERMUTE(BSWAP, v0, v1, v2, v3) \
|
||||||
|
VX v0, M0, M0 \
|
||||||
|
VX v1, M1, M1 \
|
||||||
|
VX v2, M2, M2 \
|
||||||
|
VX v3, M3, M3 \
|
||||||
|
VSTM M0, M3, off(dst)
|
||||||
|
|
||||||
|
#define SHUFFLE(a, b, c, d, t, u, v, w) \
|
||||||
|
VMRHF a, c, t \ // t = {a[0], c[0], a[1], c[1]}
|
||||||
|
VMRHF b, d, u \ // u = {b[0], d[0], b[1], d[1]}
|
||||||
|
VMRLF a, c, v \ // v = {a[2], c[2], a[3], c[3]}
|
||||||
|
VMRLF b, d, w \ // w = {b[2], d[2], b[3], d[3]}
|
||||||
|
VMRHF t, u, a \ // a = {a[0], b[0], c[0], d[0]}
|
||||||
|
VMRLF t, u, b \ // b = {a[1], b[1], c[1], d[1]}
|
||||||
|
VMRHF v, w, c \ // c = {a[2], b[2], c[2], d[2]}
|
||||||
|
VMRLF v, w, d // d = {a[3], b[3], c[3], d[3]}
|
||||||
|
|
||||||
|
// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
|
||||||
|
TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0
|
||||||
|
MOVD $·constants<>(SB), R1
|
||||||
|
MOVD dst+0(FP), R2 // R2=&dst[0]
|
||||||
|
LMG src+24(FP), R3, R4 // R3=&src[0] R4=len(src)
|
||||||
|
MOVD key+48(FP), R5 // R5=key
|
||||||
|
MOVD nonce+56(FP), R6 // R6=nonce
|
||||||
|
MOVD counter+64(FP), R7 // R7=counter
|
||||||
|
|
||||||
|
// load BSWAP and J0
|
||||||
|
VLM (R1), BSWAP, J0
|
||||||
|
|
||||||
|
// setup
|
||||||
|
MOVD $95, R0
|
||||||
|
VLM (R5), KEY0, KEY1
|
||||||
|
VLL R0, (R6), NONCE
|
||||||
|
VZERO M0
|
||||||
|
VLEIB $7, $32, M0
|
||||||
|
VSRLB M0, NONCE, NONCE
|
||||||
|
|
||||||
|
// initialize counter values
|
||||||
|
VLREPF (R7), CTR
|
||||||
|
VZERO INC
|
||||||
|
VLEIF $1, $1, INC
|
||||||
|
VLEIF $2, $2, INC
|
||||||
|
VLEIF $3, $3, INC
|
||||||
|
VAF INC, CTR, CTR
|
||||||
|
VREPIF $4, INC
|
||||||
|
|
||||||
|
chacha:
|
||||||
|
VREPF $0, J0, X0
|
||||||
|
VREPF $1, J0, X1
|
||||||
|
VREPF $2, J0, X2
|
||||||
|
VREPF $3, J0, X3
|
||||||
|
VREPF $0, KEY0, X4
|
||||||
|
VREPF $1, KEY0, X5
|
||||||
|
VREPF $2, KEY0, X6
|
||||||
|
VREPF $3, KEY0, X7
|
||||||
|
VREPF $0, KEY1, X8
|
||||||
|
VREPF $1, KEY1, X9
|
||||||
|
VREPF $2, KEY1, X10
|
||||||
|
VREPF $3, KEY1, X11
|
||||||
|
VLR CTR, X12
|
||||||
|
VREPF $1, NONCE, X13
|
||||||
|
VREPF $2, NONCE, X14
|
||||||
|
VREPF $3, NONCE, X15
|
||||||
|
|
||||||
|
MOVD $(NUM_ROUNDS/2), R1
|
||||||
|
|
||||||
|
loop:
|
||||||
|
ROUND4(X0, X4, X12, X8, X1, X5, X13, X9, X2, X6, X14, X10, X3, X7, X15, X11)
|
||||||
|
ROUND4(X0, X5, X15, X10, X1, X6, X12, X11, X2, X7, X13, X8, X3, X4, X14, X9)
|
||||||
|
|
||||||
|
ADD $-1, R1
|
||||||
|
BNE loop
|
||||||
|
|
||||||
|
// decrement length
|
||||||
|
ADD $-256, R4
|
||||||
|
|
||||||
|
// rearrange vectors
|
||||||
|
SHUFFLE(X0, X1, X2, X3, M0, M1, M2, M3)
|
||||||
|
ADDV(J0, X0, X1, X2, X3)
|
||||||
|
SHUFFLE(X4, X5, X6, X7, M0, M1, M2, M3)
|
||||||
|
ADDV(KEY0, X4, X5, X6, X7)
|
||||||
|
SHUFFLE(X8, X9, X10, X11, M0, M1, M2, M3)
|
||||||
|
ADDV(KEY1, X8, X9, X10, X11)
|
||||||
|
VAF CTR, X12, X12
|
||||||
|
SHUFFLE(X12, X13, X14, X15, M0, M1, M2, M3)
|
||||||
|
ADDV(NONCE, X12, X13, X14, X15)
|
||||||
|
|
||||||
|
// increment counters
|
||||||
|
VAF INC, CTR, CTR
|
||||||
|
|
||||||
|
// xor keystream with plaintext
|
||||||
|
XORV(0*64, R2, R3, X0, X4, X8, X12)
|
||||||
|
XORV(1*64, R2, R3, X1, X5, X9, X13)
|
||||||
|
XORV(2*64, R2, R3, X2, X6, X10, X14)
|
||||||
|
XORV(3*64, R2, R3, X3, X7, X11, X15)
|
||||||
|
|
||||||
|
// increment pointers
|
||||||
|
MOVD $256(R2), R2
|
||||||
|
MOVD $256(R3), R3
|
||||||
|
|
||||||
|
CMPBNE R4, $0, chacha
|
||||||
|
|
||||||
|
VSTEF $0, CTR, (R7)
|
||||||
|
RET
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found src the LICENSE file.
|
||||||
|
|
||||||
|
package chacha20
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
// Platforms that have fast unaligned 32-bit little endian accesses.
|
||||||
|
const unaligned = runtime.GOARCH == "386" ||
|
||||||
|
runtime.GOARCH == "amd64" ||
|
||||||
|
runtime.GOARCH == "arm64" ||
|
||||||
|
runtime.GOARCH == "ppc64le" ||
|
||||||
|
runtime.GOARCH == "s390x"
|
||||||
|
|
||||||
|
// addXor reads a little endian uint32 from src, XORs it with (a + b) and
|
||||||
|
// places the result in little endian byte order in dst.
|
||||||
|
func addXor(dst, src []byte, a, b uint32) {
|
||||||
|
_, _ = src[3], dst[3] // bounds check elimination hint
|
||||||
|
if unaligned {
|
||||||
|
// The compiler should optimize this code into
|
||||||
|
// 32-bit unaligned little endian loads and stores.
|
||||||
|
// TODO: delete once the compiler does a reliably
|
||||||
|
// good job with the generic code below.
|
||||||
|
// See issue #25111 for more details.
|
||||||
|
v := uint32(src[0])
|
||||||
|
v |= uint32(src[1]) << 8
|
||||||
|
v |= uint32(src[2]) << 16
|
||||||
|
v |= uint32(src[3]) << 24
|
||||||
|
v ^= a + b
|
||||||
|
dst[0] = byte(v)
|
||||||
|
dst[1] = byte(v >> 8)
|
||||||
|
dst[2] = byte(v >> 16)
|
||||||
|
dst[3] = byte(v >> 24)
|
||||||
|
} else {
|
||||||
|
a += b
|
||||||
|
dst[0] = src[0] ^ byte(a)
|
||||||
|
dst[1] = src[1] ^ byte(a>>8)
|
||||||
|
dst[2] = src[2] ^ byte(a>>16)
|
||||||
|
dst[3] = src[3] ^ byte(a>>24)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD and its
|
||||||
|
// extended nonce variant XChaCha20-Poly1305, as specified in RFC 8439 and
|
||||||
|
// draft-irtf-cfrg-xchacha-01.
|
||||||
|
package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// KeySize is the size of the key used by this AEAD, in bytes.
|
||||||
|
KeySize = 32
|
||||||
|
|
||||||
|
// NonceSize is the size of the nonce used with the standard variant of this
|
||||||
|
// AEAD, in bytes.
|
||||||
|
//
|
||||||
|
// Note that this is too short to be safely generated at random if the same
|
||||||
|
// key is reused more than 2³² times.
|
||||||
|
NonceSize = 12
|
||||||
|
|
||||||
|
// NonceSizeX is the size of the nonce used with the XChaCha20-Poly1305
|
||||||
|
// variant of this AEAD, in bytes.
|
||||||
|
NonceSizeX = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
type chacha20poly1305 struct {
|
||||||
|
key [KeySize]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a ChaCha20-Poly1305 AEAD that uses the given 256-bit key.
|
||||||
|
func New(key []byte) (cipher.AEAD, error) {
|
||||||
|
if len(key) != KeySize {
|
||||||
|
return nil, errors.New("chacha20poly1305: bad key length")
|
||||||
|
}
|
||||||
|
ret := new(chacha20poly1305)
|
||||||
|
copy(ret.key[:], key)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) NonceSize() int {
|
||||||
|
return NonceSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) Overhead() int {
|
||||||
|
return 16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
if len(nonce) != NonceSize {
|
||||||
|
panic("chacha20poly1305: bad nonce length passed to Seal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint64(len(plaintext)) > (1<<38)-64 {
|
||||||
|
panic("chacha20poly1305: plaintext too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.seal(dst, nonce, plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errOpen = errors.New("chacha20poly1305: message authentication failed")
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
if len(nonce) != NonceSize {
|
||||||
|
panic("chacha20poly1305: bad nonce length passed to Open")
|
||||||
|
}
|
||||||
|
if len(ciphertext) < 16 {
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
if uint64(len(ciphertext)) > (1<<38)-48 {
|
||||||
|
panic("chacha20poly1305: ciphertext too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.open(dst, nonce, ciphertext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||||
|
// slice with the contents of the given slice followed by that many bytes and a
|
||||||
|
// second slice that aliases into it and contains only the extra bytes. If the
|
||||||
|
// original slice has sufficient capacity then no allocation is performed.
|
||||||
|
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||||
|
if total := len(in) + n; cap(in) >= total {
|
||||||
|
head = in[:total]
|
||||||
|
} else {
|
||||||
|
head = make([]byte, total)
|
||||||
|
copy(head, in)
|
||||||
|
}
|
||||||
|
tail = head[len(in):]
|
||||||
|
return
|
||||||
|
}
|
87
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go
generated
vendored
Normal file
87
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gc && !purego
|
||||||
|
// +build gc,!purego
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/internal/subtle"
|
||||||
|
"golang.org/x/sys/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
|
||||||
|
|
||||||
|
var (
|
||||||
|
useAVX2 = cpu.X86.HasAVX2 && cpu.X86.HasBMI2
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupState writes a ChaCha20 input matrix to state. See
|
||||||
|
// https://tools.ietf.org/html/rfc7539#section-2.3.
|
||||||
|
func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
|
||||||
|
state[0] = 0x61707865
|
||||||
|
state[1] = 0x3320646e
|
||||||
|
state[2] = 0x79622d32
|
||||||
|
state[3] = 0x6b206574
|
||||||
|
|
||||||
|
state[4] = binary.LittleEndian.Uint32(key[0:4])
|
||||||
|
state[5] = binary.LittleEndian.Uint32(key[4:8])
|
||||||
|
state[6] = binary.LittleEndian.Uint32(key[8:12])
|
||||||
|
state[7] = binary.LittleEndian.Uint32(key[12:16])
|
||||||
|
state[8] = binary.LittleEndian.Uint32(key[16:20])
|
||||||
|
state[9] = binary.LittleEndian.Uint32(key[20:24])
|
||||||
|
state[10] = binary.LittleEndian.Uint32(key[24:28])
|
||||||
|
state[11] = binary.LittleEndian.Uint32(key[28:32])
|
||||||
|
|
||||||
|
state[12] = 0
|
||||||
|
state[13] = binary.LittleEndian.Uint32(nonce[0:4])
|
||||||
|
state[14] = binary.LittleEndian.Uint32(nonce[4:8])
|
||||||
|
state[15] = binary.LittleEndian.Uint32(nonce[8:12])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
if !cpu.X86.HasSSSE3 {
|
||||||
|
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
var state [16]uint32
|
||||||
|
setupState(&state, &c.key, nonce)
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(plaintext)+16)
|
||||||
|
if subtle.InexactOverlap(out, plaintext) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap")
|
||||||
|
}
|
||||||
|
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
if !cpu.X86.HasSSSE3 {
|
||||||
|
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
var state [16]uint32
|
||||||
|
setupState(&state, &c.key, nonce)
|
||||||
|
|
||||||
|
ciphertext = ciphertext[:len(ciphertext)-16]
|
||||||
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
|
if subtle.InexactOverlap(out, ciphertext) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap")
|
||||||
|
}
|
||||||
|
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
|
||||||
|
for i := range out {
|
||||||
|
out[i] = 0
|
||||||
|
}
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
2695
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s
generated
vendored
Normal file
2695
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
81
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go
generated
vendored
Normal file
81
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/chacha20"
|
||||||
|
"golang.org/x/crypto/internal/subtle"
|
||||||
|
"golang.org/x/crypto/poly1305"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeWithPadding(p *poly1305.MAC, b []byte) {
|
||||||
|
p.Write(b)
|
||||||
|
if rem := len(b) % 16; rem != 0 {
|
||||||
|
var buf [16]byte
|
||||||
|
padLen := 16 - rem
|
||||||
|
p.Write(buf[:padLen])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUint64(p *poly1305.MAC, n int) {
|
||||||
|
var buf [8]byte
|
||||||
|
binary.LittleEndian.PutUint64(buf[:], uint64(n))
|
||||||
|
p.Write(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
|
||||||
|
ciphertext, tag := out[:len(plaintext)], out[len(plaintext):]
|
||||||
|
if subtle.InexactOverlap(out, plaintext) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap")
|
||||||
|
}
|
||||||
|
|
||||||
|
var polyKey [32]byte
|
||||||
|
s, _ := chacha20.NewUnauthenticatedCipher(c.key[:], nonce)
|
||||||
|
s.XORKeyStream(polyKey[:], polyKey[:])
|
||||||
|
s.SetCounter(1) // set the counter to 1, skipping 32 bytes
|
||||||
|
s.XORKeyStream(ciphertext, plaintext)
|
||||||
|
|
||||||
|
p := poly1305.New(&polyKey)
|
||||||
|
writeWithPadding(p, additionalData)
|
||||||
|
writeWithPadding(p, ciphertext)
|
||||||
|
writeUint64(p, len(additionalData))
|
||||||
|
writeUint64(p, len(plaintext))
|
||||||
|
p.Sum(tag[:0])
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
tag := ciphertext[len(ciphertext)-16:]
|
||||||
|
ciphertext = ciphertext[:len(ciphertext)-16]
|
||||||
|
|
||||||
|
var polyKey [32]byte
|
||||||
|
s, _ := chacha20.NewUnauthenticatedCipher(c.key[:], nonce)
|
||||||
|
s.XORKeyStream(polyKey[:], polyKey[:])
|
||||||
|
s.SetCounter(1) // set the counter to 1, skipping 32 bytes
|
||||||
|
|
||||||
|
p := poly1305.New(&polyKey)
|
||||||
|
writeWithPadding(p, additionalData)
|
||||||
|
writeWithPadding(p, ciphertext)
|
||||||
|
writeUint64(p, len(additionalData))
|
||||||
|
writeUint64(p, len(ciphertext))
|
||||||
|
|
||||||
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||||
|
if subtle.InexactOverlap(out, ciphertext) {
|
||||||
|
panic("chacha20poly1305: invalid buffer overlap")
|
||||||
|
}
|
||||||
|
if !p.Verify(tag) {
|
||||||
|
for i := range out {
|
||||||
|
out[i] = 0
|
||||||
|
}
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
s.XORKeyStream(out, ciphertext)
|
||||||
|
return ret, nil
|
||||||
|
}
|
16
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go
generated
vendored
Normal file
16
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !amd64 || !gc || purego
|
||||||
|
// +build !amd64 !gc purego
|
||||||
|
|
||||||
|
package chacha20poly1305
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright 2018 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 chacha20poly1305
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/chacha20"
|
||||||
|
)
|
||||||
|
|
||||||
|
type xchacha20poly1305 struct {
|
||||||
|
key [KeySize]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewX returns a XChaCha20-Poly1305 AEAD that uses the given 256-bit key.
|
||||||
|
//
|
||||||
|
// XChaCha20-Poly1305 is a ChaCha20-Poly1305 variant that takes a longer nonce,
|
||||||
|
// suitable to be generated randomly without risk of collisions. It should be
|
||||||
|
// preferred when nonce uniqueness cannot be trivially ensured, or whenever
|
||||||
|
// nonces are randomly generated.
|
||||||
|
func NewX(key []byte) (cipher.AEAD, error) {
|
||||||
|
if len(key) != KeySize {
|
||||||
|
return nil, errors.New("chacha20poly1305: bad key length")
|
||||||
|
}
|
||||||
|
ret := new(xchacha20poly1305)
|
||||||
|
copy(ret.key[:], key)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*xchacha20poly1305) NonceSize() int {
|
||||||
|
return NonceSizeX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*xchacha20poly1305) Overhead() int {
|
||||||
|
return 16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
if len(nonce) != NonceSizeX {
|
||||||
|
panic("chacha20poly1305: bad nonce length passed to Seal")
|
||||||
|
}
|
||||||
|
|
||||||
|
// XChaCha20-Poly1305 technically supports a 64-bit counter, so there is no
|
||||||
|
// size limit. However, since we reuse the ChaCha20-Poly1305 implementation,
|
||||||
|
// the second half of the counter is not available. This is unlikely to be
|
||||||
|
// an issue because the cipher.AEAD API requires the entire message to be in
|
||||||
|
// memory, and the counter overflows at 256 GB.
|
||||||
|
if uint64(len(plaintext)) > (1<<38)-64 {
|
||||||
|
panic("chacha20poly1305: plaintext too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := new(chacha20poly1305)
|
||||||
|
hKey, _ := chacha20.HChaCha20(x.key[:], nonce[0:16])
|
||||||
|
copy(c.key[:], hKey)
|
||||||
|
|
||||||
|
// The first 4 bytes of the final nonce are unused counter space.
|
||||||
|
cNonce := make([]byte, NonceSize)
|
||||||
|
copy(cNonce[4:12], nonce[16:24])
|
||||||
|
|
||||||
|
return c.seal(dst, cNonce[:], plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *xchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
if len(nonce) != NonceSizeX {
|
||||||
|
panic("chacha20poly1305: bad nonce length passed to Open")
|
||||||
|
}
|
||||||
|
if len(ciphertext) < 16 {
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
if uint64(len(ciphertext)) > (1<<38)-48 {
|
||||||
|
panic("chacha20poly1305: ciphertext too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := new(chacha20poly1305)
|
||||||
|
hKey, _ := chacha20.HChaCha20(x.key[:], nonce[0:16])
|
||||||
|
copy(c.key[:], hKey)
|
||||||
|
|
||||||
|
// The first 4 bytes of the final nonce are unused counter space.
|
||||||
|
cNonce := make([]byte, NonceSize)
|
||||||
|
copy(cNonce[4:12], nonce[16:24])
|
||||||
|
|
||||||
|
return c.open(dst, cNonce[:], ciphertext, additionalData)
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// 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 hkdf implements the HMAC-based Extract-and-Expand Key Derivation
|
||||||
|
// Function (HKDF) as defined in RFC 5869.
|
||||||
|
//
|
||||||
|
// HKDF is a cryptographic key derivation function (KDF) with the goal of
|
||||||
|
// expanding limited input keying material into one or more cryptographically
|
||||||
|
// strong secret keys.
|
||||||
|
package hkdf // import "golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extract generates a pseudorandom key for use with Expand from an input secret
|
||||||
|
// and an optional independent salt.
|
||||||
|
//
|
||||||
|
// Only use this function if you need to reuse the extracted key with multiple
|
||||||
|
// Expand invocations and different context values. Most common scenarios,
|
||||||
|
// including the generation of multiple keys, should use New instead.
|
||||||
|
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
||||||
|
if salt == nil {
|
||||||
|
salt = make([]byte, hash().Size())
|
||||||
|
}
|
||||||
|
extractor := hmac.New(hash, salt)
|
||||||
|
extractor.Write(secret)
|
||||||
|
return extractor.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type hkdf struct {
|
||||||
|
expander hash.Hash
|
||||||
|
size int
|
||||||
|
|
||||||
|
info []byte
|
||||||
|
counter byte
|
||||||
|
|
||||||
|
prev []byte
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *hkdf) Read(p []byte) (int, error) {
|
||||||
|
// Check whether enough data can be generated
|
||||||
|
need := len(p)
|
||||||
|
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
||||||
|
if remains < need {
|
||||||
|
return 0, errors.New("hkdf: entropy limit reached")
|
||||||
|
}
|
||||||
|
// Read any leftover from the buffer
|
||||||
|
n := copy(p, f.buf)
|
||||||
|
p = p[n:]
|
||||||
|
|
||||||
|
// Fill the rest of the buffer
|
||||||
|
for len(p) > 0 {
|
||||||
|
f.expander.Reset()
|
||||||
|
f.expander.Write(f.prev)
|
||||||
|
f.expander.Write(f.info)
|
||||||
|
f.expander.Write([]byte{f.counter})
|
||||||
|
f.prev = f.expander.Sum(f.prev[:0])
|
||||||
|
f.counter++
|
||||||
|
|
||||||
|
// Copy the new batch into p
|
||||||
|
f.buf = f.prev
|
||||||
|
n = copy(p, f.buf)
|
||||||
|
p = p[n:]
|
||||||
|
}
|
||||||
|
// Save leftovers for next run
|
||||||
|
f.buf = f.buf[n:]
|
||||||
|
|
||||||
|
return need, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand returns a Reader, from which keys can be read, using the given
|
||||||
|
// pseudorandom key and optional context info, skipping the extraction step.
|
||||||
|
//
|
||||||
|
// The pseudorandomKey should have been generated by Extract, or be a uniformly
|
||||||
|
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
|
||||||
|
// 3.3. Most common scenarios will want to use New instead.
|
||||||
|
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
||||||
|
expander := hmac.New(hash, pseudorandomKey)
|
||||||
|
return &hkdf{expander, expander.Size(), info, 1, nil, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a Reader, from which keys can be read, using the given hash,
|
||||||
|
// secret, salt and context info. Salt and info can be nil.
|
||||||
|
func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader {
|
||||||
|
prk := Extract(hash, secret, salt)
|
||||||
|
return Expand(hash, prk, info)
|
||||||
|
}
|
|
@ -8,6 +8,10 @@ github.com/VividCortex/ewma
|
||||||
github.com/aead/chacha20/chacha
|
github.com/aead/chacha20/chacha
|
||||||
# github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635
|
# github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635
|
||||||
github.com/aead/poly1305
|
github.com/aead/poly1305
|
||||||
|
# github.com/cisco/go-hpke v0.0.0-20210215210317-01c430f1f302
|
||||||
|
## explicit
|
||||||
|
# github.com/cloudflare/odoh-go v0.1.6
|
||||||
|
## explicit
|
||||||
# github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
# github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||||
## explicit
|
## explicit
|
||||||
github.com/coreos/go-systemd/activation
|
github.com/coreos/go-systemd/activation
|
||||||
|
@ -45,6 +49,9 @@ github.com/jedisct1/go-clocksmith
|
||||||
# github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5
|
# github.com/jedisct1/go-dnsstamps v0.0.0-20210101121956-16fbdadcf8f5
|
||||||
## explicit
|
## explicit
|
||||||
github.com/jedisct1/go-dnsstamps
|
github.com/jedisct1/go-dnsstamps
|
||||||
|
# github.com/jedisct1/go-hpke-compact v0.0.0-20210329192501-7ceabaabca65
|
||||||
|
## explicit
|
||||||
|
github.com/jedisct1/go-hpke-compact
|
||||||
# github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7
|
# github.com/jedisct1/go-minisign v0.0.0-20210106175330-e54e81d562c7
|
||||||
## explicit
|
## explicit
|
||||||
github.com/jedisct1/go-minisign
|
github.com/jedisct1/go-minisign
|
||||||
|
@ -73,9 +80,12 @@ github.com/smartystreets/goconvey/convey/reporting
|
||||||
# golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
# golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/crypto/blake2b
|
golang.org/x/crypto/blake2b
|
||||||
|
golang.org/x/crypto/chacha20
|
||||||
|
golang.org/x/crypto/chacha20poly1305
|
||||||
golang.org/x/crypto/curve25519
|
golang.org/x/crypto/curve25519
|
||||||
golang.org/x/crypto/ed25519
|
golang.org/x/crypto/ed25519
|
||||||
golang.org/x/crypto/ed25519/internal/edwards25519
|
golang.org/x/crypto/ed25519/internal/edwards25519
|
||||||
|
golang.org/x/crypto/hkdf
|
||||||
golang.org/x/crypto/internal/subtle
|
golang.org/x/crypto/internal/subtle
|
||||||
golang.org/x/crypto/nacl/box
|
golang.org/x/crypto/nacl/box
|
||||||
golang.org/x/crypto/nacl/secretbox
|
golang.org/x/crypto/nacl/secretbox
|
||||||
|
|
Loading…
Reference in New Issue