chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
// Copyright 2024 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rfc5802
|
||||
|
||||
// This package is meant to implement the functions defined in RFC 5802. As such, function names will be non-standard
|
||||
// compared to most other names in the project.
|
||||
// https://datatracker.ietf.org/doc/html/rfc5802
|
||||
@@ -0,0 +1,147 @@
|
||||
// Copyright 2024 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rfc5802
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/xdg-go/stringprep"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
var (
|
||||
clientKeyConstant = OctetString("Client Key")
|
||||
serverKeyConstant = OctetString("Server Key")
|
||||
)
|
||||
|
||||
// OctetString is equivalent to a byte slice. An octet, as defined in the RFC, is an 8-bit byte. Go only supports 8-bit
|
||||
// bytes. Additionally, an octet string is defined as a sequence of octets, which we can represent as a slice.
|
||||
type OctetString []byte
|
||||
|
||||
// Base64ToOctetString returns the original octet string from its base64 encoded form.
|
||||
func Base64ToOctetString(base64String string) OctetString {
|
||||
decoded, err := base64.StdEncoding.DecodeString(base64String)
|
||||
if err != nil {
|
||||
// If we've encountered an error, then we'll return the equivalent of an empty hash. This will fail in a later step.
|
||||
return make(OctetString, 32)
|
||||
}
|
||||
return decoded
|
||||
}
|
||||
|
||||
// ClientKey returns the client key created using the salted password and a specific constant.
|
||||
func ClientKey(saltedPassword OctetString) OctetString {
|
||||
return HMAC(saltedPassword, clientKeyConstant)
|
||||
}
|
||||
|
||||
// ClientProof returns the client proof by xor'ing the client key and client signature.
|
||||
func ClientProof(clientKey OctetString, clientSignature OctetString) OctetString {
|
||||
if len(clientKey) != len(clientSignature) {
|
||||
return make(OctetString, 32)
|
||||
}
|
||||
return clientKey.Xor(clientSignature)
|
||||
}
|
||||
|
||||
// ClientSignature returns the client signature using the given stored key and auth message.
|
||||
func ClientSignature(storedKey OctetString, authMessage string) OctetString {
|
||||
return HMAC(storedKey, OctetString(authMessage))
|
||||
}
|
||||
|
||||
// H performs the SHA256 hash function, which is the hash function used by Postgres. The returned OctetString will
|
||||
// always have a length of 32.
|
||||
func H(str OctetString) OctetString {
|
||||
ret := sha256.Sum256(str)
|
||||
return ret[:]
|
||||
}
|
||||
|
||||
// Hi is, essentially, PBKDF2 with HMAC as the pseudorandom function.
|
||||
func Hi(str OctetString, salt OctetString, i uint32) OctetString {
|
||||
return pbkdf2.Key(str, salt, int(i), 32, sha256.New)
|
||||
}
|
||||
|
||||
// HMAC applies the HMAC keyed hash algorithm on the given octet strings.
|
||||
func HMAC(key OctetString, str OctetString) OctetString {
|
||||
mac := hmac.New(sha256.New, key)
|
||||
mac.Write(str)
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
// Normalize runs the SASLprep profile (https://datatracker.ietf.org/doc/html/rfc4013) of the stringprep algorithm
|
||||
// (https://datatracker.ietf.org/doc/html/rfc3454). This accepts a standard UTF8 encoded string, unlike other functions
|
||||
// which may take an OctetString.
|
||||
func Normalize(str string) (string, error) {
|
||||
return stringprep.SASLprep.Prepare(str)
|
||||
}
|
||||
|
||||
// SaltedPassword returns the salted password. The password should not have been normalized, as it is normalized within
|
||||
// the function.
|
||||
func SaltedPassword(password string, salt OctetString, i uint32) (OctetString, error) {
|
||||
normalizedPassword, err := Normalize(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Hi(OctetString(normalizedPassword), salt, i), nil
|
||||
}
|
||||
|
||||
// ServerKey returns the server key created using the salted password and a specific constant.
|
||||
func ServerKey(saltedPassword OctetString) OctetString {
|
||||
return HMAC(saltedPassword, serverKeyConstant)
|
||||
}
|
||||
|
||||
// ServerSignature returns the server signature using the given server key and auth message.
|
||||
func ServerSignature(serverKey OctetString, authMessage string) OctetString {
|
||||
return HMAC(serverKey, OctetString(authMessage))
|
||||
}
|
||||
|
||||
// StoredKey returns the stored key created using the client key.
|
||||
func StoredKey(clientKey OctetString) OctetString {
|
||||
return H(clientKey)
|
||||
}
|
||||
|
||||
// AppendInteger appends the given integer.
|
||||
func (os OctetString) AppendInteger(val uint32) OctetString {
|
||||
result := make(OctetString, len(os)+4)
|
||||
binary.BigEndian.PutUint32(result[len(os):], val)
|
||||
return result
|
||||
}
|
||||
|
||||
// Equals returns whether the calling octet string is equal to the given octet string.
|
||||
func (os OctetString) Equals(other OctetString) bool {
|
||||
return bytes.Equal(os, other)
|
||||
}
|
||||
|
||||
// ToBase64 returns the OctetString as a base64 encoded UTF8 string.
|
||||
func (os OctetString) ToBase64() string {
|
||||
return base64.StdEncoding.EncodeToString(os)
|
||||
}
|
||||
|
||||
// ToHex returns the OctetString as a hex encoded UTF8 string (lowercase).
|
||||
func (os OctetString) ToHex() string {
|
||||
return hex.EncodeToString(os)
|
||||
}
|
||||
|
||||
// Xor applies "exclusive or" for every octet between both strings. Assumes that both strings have the same length, so
|
||||
// perform any checks before calling this function.
|
||||
func (os OctetString) Xor(other OctetString) OctetString {
|
||||
result := make(OctetString, len(os))
|
||||
for i := range os {
|
||||
result[i] = os[i] ^ other[i]
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user