Skip to content
Snippets Groups Projects
Unverified Commit 1c06adb9 authored by Recolic Keghart's avatar Recolic Keghart
Browse files

init proj2

parent f8d368df
No related branches found
No related tags found
No related merge requests found
Pipeline #56 failed with stages
in 1 minute and 2 seconds
File added
package proj2
// You MUST NOT change what you import. If you add ANY additional
// imports it will break the autograder, and we will be Very Upset.
import (
// You neet to add with
// go get github.com/nweaver/cs161-p2/userlib
"github.com/nweaver/cs161-p2/userlib"
// Life is much easier with json: You are
// going to want to use this so you can easily
// turn complex structures into strings etc...
"encoding/json"
// Likewise useful for debugging etc
"encoding/hex"
// UUIDs are generated right based on the crypto RNG
// so lets make life easier and use those too...
//
// You need to add with "go get github.com/google/uuid"
"github.com/google/uuid"
// Useful for debug messages, or string manipulation for datastore keys
"strings"
// Want to import errors
"errors"
// optional
// strconv
// if you are looking for fmt, we don't give you fmt, but you can use userlib.DebugMsg
// see someUsefulThings() below
)
// This serves two purposes: It shows you some useful primitives and
// it suppresses warnings for items not being imported
func someUsefulThings() {
// Creates a random UUID
f := uuid.New()
userlib.DebugMsg("UUID as string:%v", f.String())
// Example of writing over a byte of f
f[0] = 10
userlib.DebugMsg("UUID as string:%v", f.String())
// takes a sequence of bytes and renders as hex
h := hex.EncodeToString([]byte("fubar"))
userlib.DebugMsg("The hex: %v", h)
// Marshals data into a JSON representation
// Will actually work with go structures as well
d, _ := json.Marshal(f)
userlib.DebugMsg("The json data: %v", string(d))
var g uuid.UUID
json.Unmarshal(d, &g)
userlib.DebugMsg("Unmashaled data %v", g.String())
// This creates an error type
userlib.DebugMsg("Creation of error %v", errors.New(strings.ToTitle("This is an error")))
// And a random RSA key. In this case, ignoring the error
// return value
var pk userlib.PKEEncKey
var sk userlib.PKEDecKey
pk, sk, _ = userlib.PKEKeyGen()
userlib.DebugMsg("Key is %v, %v", pk, sk)
}
// Helper function: Takes the first 16 bytes and
// converts it into the UUID type
func bytesToUUID(data []byte) (ret uuid.UUID) {
for x := range ret {
ret[x] = data[x]
}
return
}
// The structure definition for a file record
type File struct {
Filename string
FileUUID uuid.UUID
FileKey []byte
FileSlice int
}
// The structure definition for a user record
type User struct {
Username string
// You can add other fields here if you want...
// Note for JSON to marshal/unmarshal, the fields need to
// be public (start with a capital letter)
}
// This creates a user. It will only be called once for a user
// (unless the keystore and datastore are cleared during testing purposes)
// It should store a copy of the userdata, suitably encrypted, in the
// datastore and should store the user's public key in the keystore.
// The datastore may corrupt or completely erase the stored
// information, but nobody outside should be able to get at the stored
// User data: the name used in the datastore should not be guessable
// without also knowing the password and username.
// You are not allowed to use any global storage other than the
// keystore and the datastore functions in the userlib library.
// You can assume the user has a STRONG password
func InitUser(username string, password string) (userdataptr *User, err error) {
var userdata User
userdataptr = &userdata
return &userdata, nil
}
// This fetches the user information from the Datastore. It should
// fail with an error if the user/password is invalid, or if the user
// data was corrupted, or if the user can't be found.
func GetUser(username string, password string) (userdataptr *User, err error) {
var userdata User
userdataptr = &userdata
return userdataptr, nil
}
// This stores a file in the datastore.
//
// The name of the file should NOT be revealed to the datastore!
func (userdata *User) StoreFile(filename string, data []byte) {
return
}
// This adds on to an existing file.
//
// Append should be efficient, you shouldn't rewrite or reencrypt the
// existing file, but only whatever additional information and
// metadata you need.
func (userdata *User) AppendFile(filename string, data []byte) (err error) {
return
}
// This loads a file from the Datastore.
//
// It should give an error if the file is corrupted in any way.
func (userdata *User) LoadFile(filename string) (data []byte, err error) {
return
}
// You may want to define what you actually want to pass as a
// sharingRecord to serialized/deserialize in the data store.
type sharingRecord struct {
}
// This creates a sharing record, which is a key pointing to something
// in the datastore to share with the recipient.
// This enables the recipient to access the encrypted file as well
// for reading/appending.
// Note that neither the recipient NOR the datastore should gain any
// information about what the sender calls the file. Only the
// recipient can access the sharing record, and only the recipient
// should be able to know the sender.
func (userdata *User) ShareFile(filename string, recipient string) (
magic_string string, err error) {
return
}
// Note recipient's filename can be different from the sender's filename.
// The recipient should not be able to discover the sender's view on
// what the filename even is! However, the recipient must ensure that
// it is authentically from the sender.
func (userdata *User) ReceiveFile(filename string, sender string,
magic_string string) error {
return nil
}
// Removes access for all others.
func (userdata *User) RevokeFile(filename string) (err error) {
return
}
package proj2
// You MUST NOT change what you import. If you add ANY additional
// imports it will break the autograder, and we will be Very Upset.
import (
"testing"
"reflect"
"github.com/nweaver/cs161-p2/userlib"
"encoding/json"
"encoding/hex"
"github.com/google/uuid"
"strings"
"errors"
)
func TestInit(t *testing.T) {
t.Log("Initialization test")
// You may want to turn it off someday
userlib.SetDebugStatus(true)
someUsefulThings()
userlib.SetDebugStatus(false)
u, err := InitUser("alice", "fubar")
if err != nil {
// t.Error says the test fails
t.Error("Failed to initialize user", err)
}
// t.Log() only produces output if you run with "go test -v"
t.Log("Got user", u)
// If you want to comment the line above,
// write _ = u here to make the compiler happy
// You probably want many more tests here.
}
func TestStorage(t *testing.T) {
// And some more tests, because
u, err := GetUser("alice", "fubar")
if err != nil {
t.Error("Failed to reload user", err)
return
}
t.Log("Loaded user", u)
v := []byte("This is a test")
u.StoreFile("file1", v)
v2, err2 := u.LoadFile("file1")
if err2 != nil {
t.Error("Failed to upload and download", err2)
}
if !reflect.DeepEqual(v, v2) {
t.Error("Downloaded file is not the same", v, v2)
}
}
func TestShare(t *testing.T) {
u, err := GetUser("alice", "fubar")
if err != nil {
t.Error("Failed to reload user", err)
}
u2, err2 := InitUser("bob", "foobar")
if err2 != nil {
t.Error("Failed to initialize bob", err2)
}
var v, v2 []byte
var magic_string string
v, err = u.LoadFile("file1")
if err != nil {
t.Error("Failed to download the file from alice", err)
}
magic_string, err = u.ShareFile("file1", "bob")
if err != nil {
t.Error("Failed to share the a file", err)
}
err = u2.ReceiveFile("file2", "alice", magic_string)
if err != nil {
t.Error("Failed to receive the share message", err)
}
v2, err = u2.LoadFile("file2")
if err != nil {
t.Error("Failed to download the file after sharing", err)
}
if !reflect.DeepEqual(v, v2) {
t.Error("Shared file is not the same", v, v2)
}
}
package userlib
import (
"fmt"
"strings"
"time"
"errors"
"log"
"io"
"crypto"
"crypto/rsa"
"crypto/hmac"
"crypto/rand"
"crypto/sha512"
"crypto/aes"
"crypto/cipher"
"golang.org/x/crypto/argon2"
"github.com/google/uuid"
)
type UUID = uuid.UUID
// RSA key size
var RSAKeySize = 2048
// AES block size and key size
var AESBlockSize = aes.BlockSize
var AESKeySize = 16
// Hash and MAC size
var HashSize = sha512.Size
// Debug print true/false
var DebugPrint = false
// DebugMsg. Helper function: Does formatted printing to stderr if
// the DebugPrint global is set. All our testing ignores stderr,
// so feel free to use this for any sort of testing you want.
func SetDebugStatus(status bool){
DebugPrint = status
}
func DebugMsg(format string, args ...interface{}) {
if DebugPrint {
msg := fmt.Sprintf("%v ", time.Now().Format("15:04:05.00000"))
log.Printf(msg+strings.Trim(format, "\r\n ")+"\n", args...)
}
}
// RandomBytes. Helper function: Returns a byte slice of the specificed
// size filled with random data
func RandomBytes(bytes int) (data []byte) {
data = make([]byte, bytes)
if _, err := io.ReadFull(rand.Reader, data); err != nil {
panic(err)
}
return
}
type PublicKeyType struct {
KeyType string
PubKey rsa.PublicKey
}
type PrivateKeyType struct {
KeyType string
PrivKey rsa.PrivateKey
}
// Datastore and Keystore variables
var datastore map[UUID][]byte = make(map[UUID][]byte)
var keystore map[string]PublicKeyType = make(map[string]PublicKeyType)
/*
********************************************
** Datastore Functions **
** DatastoreSet, DatastoreGet, **
** DatastoreDelete, DatastoreClear **
********************************************
*/
// Sets the value in the datastore
func DatastoreSet(key UUID, value []byte) {
foo := make([]byte, len(value))
copy(foo, value)
datastore[key] = foo
}
// Returns the value if it exists
func DatastoreGet(key UUID) (value []byte, ok bool) {
value, ok = datastore[key]
if ok && value != nil {
foo := make([]byte, len(value))
copy(foo, value)
return foo, ok
}
return
}
// Deletes a key
func DatastoreDelete(key UUID) {
delete(datastore, key)
}
// Use this in testing to reset the datastore to empty
func DatastoreClear() {
datastore = make(map[UUID][]byte)
}
// Use this in testing to reset the keystore to empty
func KeystoreClear() {
keystore = make(map[string]PublicKeyType)
}
// Sets the value in the keystore
func KeystoreSet(key string, value PublicKeyType) error {
_, present := keystore[key]
if present != false {
return errors.New("That entry in the Keystore has been taken.")
}
keystore[key] = value
return nil
}
// Returns the value if it exists
func KeystoreGet(key string) (value PublicKeyType, ok bool) {
value, ok = keystore[key]
return
}
// Use this in testing to get the underlying map if you want
// to play with the datastore.
func DatastoreGetMap() map[UUID][]byte {
return datastore
}
// Use this in testing to get the underlying map if you want
// to play with the keystore.
func KeystoreGetMap() map[string]PublicKeyType {
return keystore
}
/*
********************************************
** Public Key Encryption **
** PKEKeyGen, PKEEnc, PKEDec **
********************************************
*/
// Four structs to help you manage your different keys
// You should only have 1 of each struct
// keyType should be either:
// "PKE": encryption
// "DS": authentication and integrity
type PKEEncKey = PublicKeyType
type PKEDecKey = PrivateKeyType
type DSSignKey = PrivateKeyType
type DSVerifyKey = PublicKeyType
// Generates a key pair for public-key encryption via RSA
func PKEKeyGen() (PKEEncKey, PKEDecKey, error) {
RSAPrivKey, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
RSAPubKey := RSAPrivKey.PublicKey
var PKEEncKeyRes PKEEncKey
PKEEncKeyRes.KeyType = "PKE"
PKEEncKeyRes.PubKey = RSAPubKey
var PKEDecKeyRes PKEDecKey
PKEDecKeyRes.KeyType = "PKE"
PKEDecKeyRes.PrivKey = *RSAPrivKey
return PKEEncKeyRes, PKEDecKeyRes, err
}
// Encrypts a byte stream via RSA-OAEP with sha512 as hash
func PKEEnc(ek PKEEncKey, plaintext []byte) ([]byte, error) {
RSAPubKey := &ek.PubKey
if ek.KeyType != "PKE" {
return nil, errors.New("Using a non-PKE key for PKE.")
}
ciphertext, err := rsa.EncryptOAEP(sha512.New(), rand.Reader, RSAPubKey, plaintext, nil)
return ciphertext, err
}
// Decrypts a byte stream encrypted with RSA-OAEP/sha512
func PKEDec(dk PKEDecKey, ciphertext []byte) ([]byte, error) {
RSAPrivKey := &dk.PrivKey
if dk.KeyType != "PKE" {
return nil, errors.New("Using a non-PKE key for PKE.")
}
decryption, err := rsa.DecryptOAEP(sha512.New(), rand.Reader, RSAPrivKey, ciphertext, nil)
return decryption, err
}
/*
********************************************
** Digital Signature **
** DSKeyGen, DSSign, DSVerify **
********************************************
*/
// Generates a key pair for digital signature via RSA
func DSKeyGen() (DSSignKey, DSVerifyKey, error) {
RSAPrivKey, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
RSAPubKey := RSAPrivKey.PublicKey
var DSSignKeyRes DSSignKey
DSSignKeyRes.KeyType = "DS"
DSSignKeyRes.PrivKey = *RSAPrivKey
var DSVerifyKeyRes DSVerifyKey
DSVerifyKeyRes.KeyType = "DS"
DSVerifyKeyRes.PubKey = RSAPubKey
return DSSignKeyRes, DSVerifyKeyRes, err
}
// Signs a byte stream via SHA256 and PKCS1v15
func DSSign(sk DSSignKey, msg []byte) ([]byte, error) {
RSAPrivKey := &sk.PrivKey
if sk.KeyType != "DS" {
return nil, errors.New("Using a non-DS key for DS.")
}
hashed := sha512.Sum512(msg)
sig, err := rsa.SignPKCS1v15(rand.Reader, RSAPrivKey, crypto.SHA512, hashed[:])
return sig, err
}
// Verifies a signature signed with SHA256 and PKCS1v15
func DSVerify(vk DSVerifyKey, msg []byte, sig []byte) error {
RSAPubKey := &vk.PubKey
if vk.KeyType != "DS" {
return errors.New("Using a non-DS key for DS.")
}
hashed := sha512.Sum512(msg)
err := rsa.VerifyPKCS1v15(RSAPubKey, crypto.SHA512, hashed[:], sig)
return err
}
/*
********************************************
** HMAC **
** HMACEval, HMACEqual **
********************************************
*/
// Evaluate the HMAC using sha512
func HMACEval(key []byte, msg []byte) ([]byte, error) {
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
panic(errors.New("The input as key for HMAC should be a 16-byte key."))
}
mac := hmac.New(sha512.New, key)
mac.Write(msg)
res := mac.Sum(nil)
return res, nil
}
// Equals comparison for hashes/MACs
// Does NOT leak timing.
func HMACEqual(a []byte, b []byte) bool {
return hmac.Equal(a, b)
}
/*
********************************************
** KDF **
** Argon2Key **
********************************************
*/
// Argon2: Automatically choses a decent combination of iterations and memory
// Use this to generate a key from a password
func Argon2Key(password []byte, salt []byte, keyLen uint32) []byte {
return argon2.IDKey(password, salt, 1, 64*1024, 4, keyLen)
}
/*
********************************************
** Symmetric Encryption **
** SymEnc, SymDec **
********************************************
*/
// Encrypts a byte slice with AES-CTR
// Length of iv should be == AESBlockSize
func SymEnc(key []byte, iv []byte, plaintext []byte) []byte {
if len(iv) != AESBlockSize {
panic("IV length not equal to AESBlockSize")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
stream := cipher.NewCTR(block, iv)
ciphertext := make([]byte, AESBlockSize + len(plaintext))
copy(ciphertext[:AESBlockSize], iv)
stream.XORKeyStream(ciphertext[AESBlockSize:], plaintext)
return ciphertext
}
// Decrypts a ciphertext encrypted with AES-CTR
func SymDec(key []byte, ciphertext []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
iv := ciphertext[:AESBlockSize]
plaintext := make([]byte, len(ciphertext) - AESBlockSize)
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(plaintext, ciphertext[aes.BlockSize:])
return plaintext
}
package userlib
import "testing"
import "bytes"
import "encoding/hex"
import "github.com/google/uuid"
// Golang has a very powerful routine for building tests.
// Run with "go test" to run the tests
// And "go test -v" to run verbosely so you see all the logging and
// what tests pass/fail individually.
// And "go test -cover" to check your code coverage in your tests
// Default test strings
var key1 []byte = []byte("cs161teststring1")
var key2 []byte = []byte("cs161teststring2")
var key3 []byte = []byte("cs161teststring3")
var key4 []byte = []byte("cs161teststring4")
var key5 []byte = []byte("cs161teststring5")
// Creates a UUID from the supplied bytes
// Use for testing only!
func UUIDFromBytes(t *testing.T, b []byte) (u UUID) {
u, err := uuid.FromBytes(b)
if err != nil {
t.Error("Got FromBytes error:", err)
}
return
}
func TestUUIDFromBytesDeterministic(t *testing.T) {
UUID1 := UUIDFromBytes(t, key1)
t.Log(UUID1)
UUID2 := UUIDFromBytes(t, key1)
t.Log(UUID2)
if UUID1 != UUID2 {
t.Error("UUID1 != UUID2")
t.Log("UUID1:", UUID1)
t.Log("UUID2:", UUID2)
}
}
func TestDatastore(t *testing.T) {
UUID1 := UUIDFromBytes(t, key1)
UUID2 := UUIDFromBytes(t, key2)
UUID3 := UUIDFromBytes(t, key3)
DatastoreSet(UUID1, []byte("foo"))
_, valid := DatastoreGet(UUID3)
if valid {
t.Error("Datastore fetched UUID3 when it wasn't supposed to")
}
data, valid := DatastoreGet(UUID1)
if !valid || string(data) != "foo" {
t.Error("Error with fetching 'foo' from UUID1")
}
_, valid = DatastoreGet(UUID3)
if valid {
t.Error("Returned when nothing, oops")
}
DatastoreSet(UUID2, []byte("bar"))
data, valid = DatastoreGet(UUID1)
if !valid || string(data) != "foo" {
t.Error("Error with fetching 'foo' from UUID1")
}
DatastoreDelete(UUID1)
_, valid = DatastoreGet(UUID1)
if valid {
t.Error("DatastoreGet succeeded even after deleting UUID1")
}
data, valid = DatastoreGet(UUID2)
if !valid || string(data) != "bar" {
t.Error("Error with fetching 'bar' from UUID2")
}
DatastoreClear()
_, valid = DatastoreGet(UUID2)
if valid {
t.Error("DatastoreGet succeeded even after DatastoreClear")
}
t.Log("Datastore fetch", data)
t.Log("Datastore map", DatastoreGetMap())
DatastoreClear()
t.Log("Datastore map", DatastoreGetMap())
}
func TestKeystore(t *testing.T) {
RSAPubKey, _, err1 := PKEKeyGen()
_, DSVerifyKey, err2 := DSKeyGen()
if err1 != nil || err2 != nil {
t.Error("PKEKeyGen() failed")
}
KeystoreSet("user1", RSAPubKey)
KeystoreSet("user2", DSVerifyKey)
_, valid := KeystoreGet("user3")
if valid {
t.Error("Keystore fetched UUID3 when it wasn't supposed to")
}
data, valid := KeystoreGet("user1")
if !valid {
t.Error("Key stored at UUID1 doesn't match")
}
data, valid = KeystoreGet("user2")
if !valid {
t.Error("Key stored at UUID2 doesn't match")
}
KeystoreClear()
_, valid = KeystoreGet("user1")
if valid {
t.Error("KeystoreGet succeeded even after KeystoreClear")
}
t.Log("Keystore fetch", data)
t.Log("Keystore map", KeystoreGetMap())
KeystoreClear()
t.Log("Keystore map", KeystoreGetMap())
}
func TestRSA(t *testing.T) {
// Test RSA Encrypt and Decrypt
RSAPubKey, RSAPrivKey, err := PKEKeyGen()
if err != nil {
t.Error("PKEKeyGen() failed", err)
}
t.Log(RSAPubKey)
ciphertext, err := PKEEnc(RSAPubKey, []byte("Squeamish Ossifrage"))
if err != nil {
t.Error("PKEEnc() error", err)
}
decryption, err := PKEDec(RSAPrivKey, ciphertext)
if err != nil || (string(decryption) != "Squeamish Ossifrage") {
t.Error("Decryption failed", err)
}
// Test RSA Sign and Verify
DSSignKey, DSVerifyKey, err := DSKeyGen()
if err != nil {
t.Error("DSKeyGen() failed", err)
}
sign, err := DSSign(DSSignKey, []byte("Squeamish Ossifrage"))
if err != nil {
t.Error("RSA sign failure")
}
err = DSVerify(DSVerifyKey, []byte("Squeamish Ossifrage"), sign)
if err != nil {
t.Error("RSA verification failure")
}
err = DSVerify(DSVerifyKey, []byte("foo"), sign)
if err == nil {
t.Error("RSA verification worked when it shouldn't")
}
t.Log("Error return", err)
}
func TestHMAC(t *testing.T) {
msga := []byte("foo")
msgb := []byte("bar")
hmac1a, _ := HMACEval(key1, msga)
hmac1b, _ := HMACEval(key1, msgb)
if HMACEqual(hmac1a, hmac1b) {
t.Error("HMACs are equal for different data")
}
hmac2a, _ := HMACEval(key2, msga)
if HMACEqual(hmac1a, hmac2a) {
t.Error("HMACs are equal for different key")
}
hmac1a2, _ := HMACEval(key1, msga)
if !HMACEqual(hmac1a, hmac1a2) {
t.Error("HMACs are not equal when they should be")
}
}
func TestArgon2(t *testing.T) {
val1 := Argon2Key([]byte("Password"), []byte("nosalt"), 32)
val2 := Argon2Key([]byte("Password"), []byte("nosalt"), 64)
val3 := Argon2Key([]byte("password"), []byte("nosalt"), 32)
equal := bytes.Equal
if equal(val1, val2) || equal(val1, val3) || equal(val2, val3) {
t.Error("Argon2 problem")
}
t.Log(hex.EncodeToString(val1))
t.Log(hex.EncodeToString(val2))
t.Log(hex.EncodeToString(val3))
}
func TestStreamCipher(t *testing.T) {
iv := RandomBytes(16)
t.Log("Random IV", iv)
ciphertext := SymEnc(key1, iv, []byte("foo"))
decryption := SymDec(key1, ciphertext)
t.Log("Decrypted messagege:", string(decryption))
if string(decryption) != "foo" {
t.Error("Symmetric decryption failure")
}
}
// Deliberate fail example
// func TestFailure(t *testing.T){
// t.Log("This test will fail")
// t.Error("Test of failure")
//}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment