in the beginning, there was darkness

This commit is contained in:
Kyle Isom 2025-05-09 17:17:55 -07:00
commit 308e237fc6
6 changed files with 158 additions and 0 deletions

47
README.org Normal file
View File

@ -0,0 +1,47 @@
#+title: MCIAS
#+created: <2025-05-09 Fri 13:42>
* MCIAS
MCIAS is the metacircular identity and access system.
It currently provides the following across metacircular services:
1. User password authentication.
2. User token authentication.
3. Database credential authentication.
Future work should consider adding support for:
1. TOTP
2. Policy management.
** API endpoints
*** The login type
The general datastructure used to log in should look like:
#+begin_src: json
{
"version": "v1",
"login": {
"user": "username",
"password": "secret password",
"token": "1234567890",
"totp": "123456"
}
}
#+end_src
Any fields that aren't used should be omitted. The =version= and
=login.user= types are required, as well as the appropriate
credential field.
*** =/v1/login/password=
The request should be a JSON object:
*** =/v1/login/token=
*** =/v1/credentials/database=

13
data/rand.go Normal file
View File

@ -0,0 +1,13 @@
package data
const saltLength = 32
func Salt() ([]byte, error) {
salt := make([]byte, saltLength)
_, err := rand.Read(salt)
if err != nil {
return nil, err
}
return salt, nil
}

61
data/user.go Normal file
View File

@ -0,0 +1,61 @@
package data
const (
scryptN = 32768
scriptR = 8
scryptP = 1
)
type User struct {
ID string
Created int64
User string
Password []byte
Salt []byte
}
type Login struct {
User string `json:"user"`
Password string `json:"password,omitzero"`
Token string `json:"token,omitzero"`
}
func derive(password string, salt []byte) []byte {
return scrypt.Key(login.Password, u.Salt, scryptN, scryptR, scryptN, 32)
}
func (u *User) Check(login *Login) bool {
if u.User != login.User {
return false
}
derived := derive(login.Password, u.Salt)
if subtle.ConstantTimeCompare(derived, u.Password) != 0 {
return false
}
return true
}
func (u *User) Register(login *Login) error {
var err error
if u.User != "" && u.User != login.User {
return errors.New("invalid user")
}
if u.ID == "" {
u.ID = ulid.Make()
}
u.User = login.User
u.Salt, err = Salt()
if err != nil {
return fmt.Errorf("failed to register user: %w", err)
}
u.Password = derive(login.Password, u.Salt)
return nil
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module git.wntrmute.dev/kyle/mcias
go 1.23.8
require github.com/oklog/ulid/v2 v2.1.0 // indirect

3
go.sum Normal file
View File

@ -0,0 +1,3 @@
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=

29
schema.sql Normal file
View File

@ -0,0 +1,29 @@
CREATE TABLE users (
id text primary key,
created integer,
user text not null,
password blob not null,
salt blob not null
);
CREATE TABLE tokens (
id text primary key,
uid text not null,
token text not null,
expires integer default 0,
FOREIGN KEY(uid) REFERENCES user(id)
);
CREATE TABLE database (
id text primary key,
host text not null,
port integer default 5432,
name text not null,
user text not null,
password text not null
);
CREATE TABLE registrations (
id text primary key,
code text not null
);