mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 18:48:33 -04:00
refactor: Switched from age to libsodium.
This commit is contained in:
255
ssh.odin
Normal file
255
ssh.odin
Normal file
@@ -0,0 +1,255 @@
|
||||
package main
|
||||
|
||||
import "core:encoding/base64"
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
SSH_ED25519 :: "ssh-ed25519"
|
||||
|
||||
Ed25519Keypair :: struct {
|
||||
Public: [32]u8,
|
||||
Private: [32]u8,
|
||||
}
|
||||
|
||||
read_wire_string :: proc(data: []u8, offset: ^int) -> (s: string, ok: bool) {
|
||||
if offset^ + 4 > len(data) {
|
||||
return
|
||||
}
|
||||
length := u32(data[offset^]) << 24 | u32(data[offset^ + 1]) << 16 |
|
||||
u32(data[offset^ + 2]) << 8 | u32(data[offset^ + 3])
|
||||
offset^ += 4
|
||||
|
||||
if offset^ + int(length) > len(data) {
|
||||
return
|
||||
}
|
||||
|
||||
s = string(data[offset^ : offset^ + int(length)])
|
||||
offset^ += int(length)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
parse_ssh_public_key :: proc(pub_path: string) -> (pub: [32]u8, ok: bool) {
|
||||
data, err := os.read_entire_file_from_path(pub_path, context.temp_allocator)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
text := strings.trim_right(string(data), "\n")
|
||||
parts := strings.split(text, " ", context.temp_allocator)
|
||||
if len(parts) < 2 {
|
||||
return
|
||||
}
|
||||
if parts[0] != SSH_ED25519 {
|
||||
return
|
||||
}
|
||||
|
||||
decoded, decode_err := base64.decode(parts[1], allocator = context.temp_allocator)
|
||||
if decode_err != nil || len(decoded) < 51 {
|
||||
return
|
||||
}
|
||||
|
||||
offset := 0
|
||||
key_type, type_ok := read_wire_string(decoded, &offset)
|
||||
if !type_ok || key_type != SSH_ED25519 {
|
||||
return
|
||||
}
|
||||
|
||||
pk_data, pk_ok := read_wire_string(decoded, &offset)
|
||||
if !pk_ok || len(pk_data) != 32 {
|
||||
return
|
||||
}
|
||||
|
||||
for i in 0 ..< 32 {
|
||||
pub[i] = pk_data[i]
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
parse_ssh_private_key :: proc(priv_path: string) -> (kp: Ed25519Keypair, ok: bool) {
|
||||
data, err := os.read_entire_file_from_path(priv_path, context.temp_allocator)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
text := string(data)
|
||||
lines := strings.split(text, "\n", context.temp_allocator)
|
||||
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b, context.temp_allocator)
|
||||
defer strings.builder_destroy(&b)
|
||||
|
||||
in_block := false
|
||||
for line in lines {
|
||||
trimmed := strings.trim_space(line)
|
||||
if trimmed == "-----BEGIN OPENSSH PRIVATE KEY-----" {
|
||||
in_block = true
|
||||
continue
|
||||
}
|
||||
if trimmed == "-----END OPENSSH PRIVATE KEY-----" {
|
||||
break
|
||||
}
|
||||
if in_block && len(trimmed) > 0 {
|
||||
fmt.sbprintf(&b, "%s", trimmed)
|
||||
}
|
||||
}
|
||||
|
||||
b64_str := strings.to_string(b)
|
||||
decoded, decode_err := base64.decode(b64_str, allocator = context.temp_allocator)
|
||||
if decode_err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
magic := "openssh-key-v1\x00"
|
||||
if len(decoded) < len(magic) {
|
||||
return
|
||||
}
|
||||
for i in 0 ..< len(magic) {
|
||||
if decoded[i] != u8(magic[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
offset := len(magic)
|
||||
|
||||
ciphername, cipher_ok := read_wire_string(decoded, &offset)
|
||||
if !cipher_ok || ciphername != "none" {
|
||||
return
|
||||
}
|
||||
|
||||
kdfname, kdf_ok := read_wire_string(decoded, &offset)
|
||||
if !kdf_ok || kdfname != "none" {
|
||||
return
|
||||
}
|
||||
|
||||
_, opts_ok := read_wire_string(decoded, &offset)
|
||||
if !opts_ok {
|
||||
return
|
||||
}
|
||||
|
||||
if offset + 4 > len(decoded) {
|
||||
return
|
||||
}
|
||||
num_keys := u32(decoded[offset]) << 24 | u32(decoded[offset + 1]) << 16 |
|
||||
u32(decoded[offset + 2]) << 8 | u32(decoded[offset + 3])
|
||||
offset += 4
|
||||
|
||||
if num_keys != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
_, pub_blob_ok := read_wire_string(decoded, &offset)
|
||||
if !pub_blob_ok {
|
||||
return
|
||||
}
|
||||
|
||||
priv_blob, priv_blob_ok := read_wire_string(decoded, &offset)
|
||||
if !priv_blob_ok {
|
||||
return
|
||||
}
|
||||
|
||||
inner_offset := 0
|
||||
if inner_offset + 8 > len(priv_blob) {
|
||||
return
|
||||
}
|
||||
check1 := u32(priv_blob[inner_offset]) << 24 | u32(priv_blob[inner_offset + 1]) << 16 |
|
||||
u32(priv_blob[inner_offset + 2]) << 8 | u32(priv_blob[inner_offset + 3])
|
||||
inner_offset += 4
|
||||
check2 := u32(priv_blob[inner_offset]) << 24 | u32(priv_blob[inner_offset + 1]) << 16 |
|
||||
u32(priv_blob[inner_offset + 2]) << 8 | u32(priv_blob[inner_offset + 3])
|
||||
inner_offset += 4
|
||||
|
||||
if check1 != check2 {
|
||||
return
|
||||
}
|
||||
|
||||
priv_type, type_ok := read_wire_string(transmute([]u8)priv_blob, &inner_offset)
|
||||
if !type_ok || priv_type != SSH_ED25519 {
|
||||
return
|
||||
}
|
||||
|
||||
pub_wire, pub_ok := read_wire_string(transmute([]u8)priv_blob, &inner_offset)
|
||||
if !pub_ok || len(pub_wire) != 32 {
|
||||
return
|
||||
}
|
||||
for i in 0 ..< 32 {
|
||||
kp.Public[i] = pub_wire[i]
|
||||
}
|
||||
|
||||
priv_wire, priv_ok := read_wire_string(transmute([]u8)priv_blob, &inner_offset)
|
||||
if !priv_ok || len(priv_wire) != 64 {
|
||||
return
|
||||
}
|
||||
for i in 0 ..< 32 {
|
||||
kp.Private[i] = priv_wire[i]
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
is_ed25519_key :: proc(priv_path: string) -> bool {
|
||||
pub_path, _ := strings.concatenate([]string{priv_path, ".pub"}, context.temp_allocator)
|
||||
_, ok := parse_ssh_public_key(pub_path)
|
||||
return ok
|
||||
}
|
||||
|
||||
is_encrypted_key :: proc(priv_path: string) -> bool {
|
||||
data, err := os.read_entire_file_from_path(priv_path, context.temp_allocator)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if !strings.contains(string(data), "BEGIN OPENSSH PRIVATE KEY") {
|
||||
return true
|
||||
}
|
||||
|
||||
text := string(data)
|
||||
lines := strings.split(text, "\n", context.temp_allocator)
|
||||
|
||||
b2: strings.Builder
|
||||
strings.builder_init(&b2, context.temp_allocator)
|
||||
defer strings.builder_destroy(&b2)
|
||||
|
||||
in_block := false
|
||||
for line in lines {
|
||||
trimmed := strings.trim_space(line)
|
||||
if trimmed == "-----BEGIN OPENSSH PRIVATE KEY-----" {
|
||||
in_block = true
|
||||
continue
|
||||
}
|
||||
if trimmed == "-----END OPENSSH PRIVATE KEY-----" {
|
||||
break
|
||||
}
|
||||
if in_block && len(trimmed) > 0 {
|
||||
fmt.sbprintf(&b2, "%s", trimmed)
|
||||
}
|
||||
}
|
||||
|
||||
b64_str := strings.to_string(b2)
|
||||
decoded, decode_err := base64.decode(b64_str, allocator = context.temp_allocator)
|
||||
if decode_err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
magic := "openssh-key-v1\x00"
|
||||
if len(decoded) < len(magic) {
|
||||
return true
|
||||
}
|
||||
for i in 0 ..< len(magic) {
|
||||
if decoded[i] != u8(magic[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
offset := len(magic)
|
||||
ciphername, cipher_ok := read_wire_string(decoded, &offset)
|
||||
if !cipher_ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return ciphername != "none"
|
||||
}
|
||||
Reference in New Issue
Block a user