やっとTOTP機能性を修正した
このコミットが含まれているのは:
コミット
55543618e5
|
@ -1,3 +1,7 @@
|
|||
# 1.2.0
|
||||
* やっとTOTP機能性を修正した
|
||||
* makeを実行したら、バイナリがもっと小さくなる
|
||||
|
||||
# 1.1.2
|
||||
* OpenBSDでのコンパイラーが発生された問題を修正した
|
||||
|
||||
|
|
4
Makefile
4
Makefile
|
@ -1,9 +1,9 @@
|
|||
NAME=sp
|
||||
VERSION=1.1.2
|
||||
VERSION=1.2.0
|
||||
# Linux、Haiku、かIllumos = /usr、FreeBSDかOpenBSD = /usr/local、NetBSD = /usr/pkg
|
||||
PREFIX=/usr
|
||||
CC=cc
|
||||
FILES=main.c showpass.c yankpass.c addpass.c delpass.c listpass.c genpass.c initpass.c otppass.c
|
||||
FILES=main.c showpass.c yankpass.c addpass.c delpass.c listpass.c genpass.c initpass.c otppass.c base32.c
|
||||
CFLAGS=-Wall -Wextra -g -I${PREFIX}/include -L${PREFIX}/lib
|
||||
LDFLAGS=-lgpgme -lcrypto
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#include "base32.h"
|
||||
|
||||
int char_to_val(char c) {
|
||||
const char *base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
char *ptr = strchr(base32_alphabet, c);
|
||||
return ptr ? ptr - base32_alphabet : -1;
|
||||
}
|
||||
|
||||
unsigned char *base32_decode(const char *encoded, size_t *out_len) {
|
||||
size_t encoded_len = strlen(encoded);
|
||||
size_t padding = 0;
|
||||
for (int i = encoded_len - 1; i >= 0 && encoded[i] == '='; --i) {
|
||||
++padding;
|
||||
}
|
||||
|
||||
*out_len = (encoded_len - padding) * 5 / 8;
|
||||
if (*out_len == 0) return NULL;
|
||||
|
||||
unsigned char *decoded = malloc(*out_len);
|
||||
if (!decoded) return NULL;
|
||||
|
||||
int buffer = 0, bits_left = 0, count = 0;
|
||||
for (size_t i = 0; i < encoded_len - padding; ++i) {
|
||||
int val = char_to_val(encoded[i]);
|
||||
if (val < 0) {
|
||||
free(decoded);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer <<= 5;
|
||||
buffer |= val;
|
||||
bits_left += 5;
|
||||
|
||||
if (bits_left >= 8) {
|
||||
decoded[count++] = (unsigned char) (buffer >> (bits_left - 8));
|
||||
bits_left -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = count;
|
||||
return decoded;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef BASE32_H
|
||||
#define BASE32_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
unsigned char *base32_decode(const char *encoded, size_t *out_len);
|
||||
|
||||
#endif
|
16
main.c
16
main.c
|
@ -16,7 +16,7 @@
|
|||
void helpme();
|
||||
|
||||
const char* sofname = "sp";
|
||||
const char* version = "1.1.2";
|
||||
const char* version = "1.2.0";
|
||||
|
||||
void helpme() {
|
||||
printf("076 sp - シンプルなパスワードマネージャー\n");
|
||||
|
@ -64,7 +64,19 @@ int main (int argc, char* argv[]) {
|
|||
else if (argc == 4 && strcmp(argv[3], "secure") == 0) genpass(atoi(argv[2]), true);
|
||||
else helpme();
|
||||
}
|
||||
else if (argc == 3 && strcmp(argv[1], "-o") == 0) otppass(argv[2]);
|
||||
else if (argc == 3 && strcmp(argv[1], "-o") == 0) {
|
||||
char fullPath[512];
|
||||
char* homedir = getenv("HOME");
|
||||
if (homedir == NULL) {
|
||||
perror("ホームディレクトリを受取に失敗。");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* basedir = "/.local/share/sp/";
|
||||
snprintf(fullPath, sizeof(fullPath), "%s%s%s.gpg", homedir, basedir, argv[2]);
|
||||
|
||||
otppass(fullPath);
|
||||
}
|
||||
else if (argc == 2 && strcmp(argv[1], "-v") == 0) printf("%s-%s\n", sofname, version);
|
||||
else helpme();
|
||||
|
||||
|
|
131
otppass.c
131
otppass.c
|
@ -1,26 +1,123 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <gpgme.h>
|
||||
|
||||
unsigned long generate_totp(const unsigned char* secret, size_t keylen) {
|
||||
unsigned long timestep = time(NULL) / 30;
|
||||
unsigned char hmacres[20];
|
||||
#include "base32.h"
|
||||
#include "otppass.h"
|
||||
|
||||
HMAC(EVP_sha1(), secret, keylen, (unsigned char*)×tep, sizeof(timestep), hmacres, NULL);
|
||||
unsigned char* extract_secret(const char* otpauth_url, size_t* decoded_len) {
|
||||
const char* secret_start = strstr(otpauth_url, "secret=");
|
||||
if (!secret_start) {
|
||||
fprintf(stderr, "OTPAuth URLの中に、シークレットを見つけられませんでした。\n");
|
||||
return NULL;
|
||||
}
|
||||
secret_start += 7;
|
||||
|
||||
int offset = hmacres[19] & 0xF;
|
||||
unsigned long trunhash = (hmacres[offset] & 0x7F) << 24 |
|
||||
(hmacres[offset + 1] & 0xFF) << 16 |
|
||||
(hmacres[offset + 2] & 0xFF) << 8 |
|
||||
(hmacres[offset + 3] & 0xFF);
|
||||
|
||||
unsigned long otp = trunhash % 1000000;
|
||||
return otp;
|
||||
const char* secret_end = strchr(secret_start, '&');
|
||||
if (!secret_end) {
|
||||
if (secret_start[0] != '\0') {
|
||||
secret_end = secret_start + strlen(secret_start);
|
||||
} else {
|
||||
secret_end = secret_start;
|
||||
}
|
||||
}
|
||||
|
||||
if (secret_end < secret_start) {
|
||||
fprintf(stderr, "不正なシークレットの距離。\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t secret_len = secret_end - secret_start;
|
||||
char* secret_encoded = (char*)malloc(secret_len + 1);
|
||||
|
||||
if (secret_encoded == NULL) {
|
||||
fprintf(stderr, "メモリの役割に失敗。\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strncpy(secret_encoded, secret_start, secret_len);
|
||||
secret_encoded[secret_len] = '\0';
|
||||
|
||||
unsigned char* secret_decoded = base32_decode(secret_encoded, decoded_len);
|
||||
free(secret_encoded);
|
||||
|
||||
if (!secret_decoded) {
|
||||
fprintf(stderr, "BASE32の復号化に失敗。\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return secret_decoded;
|
||||
}
|
||||
|
||||
uint32_t generate_totp(const char *secret, uint64_t counter) {
|
||||
counter = htobe64(counter);
|
||||
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
HMAC(EVP_sha1(), secret, strlen(secret), (unsigned char *)&counter, sizeof(counter), hash, NULL);
|
||||
|
||||
int offset = hash[SHA_DIGEST_LENGTH - 1] & 0x0F;
|
||||
uint32_t truncated_hash =
|
||||
(hash[offset] & 0x7F) << 24 |
|
||||
(hash[offset + 1] & 0xFF) << 16 |
|
||||
(hash[offset + 2] & 0xFF) << 8 |
|
||||
(hash[offset + 3] & 0xFF);
|
||||
|
||||
return truncated_hash % 1000000;
|
||||
}
|
||||
|
||||
void otppass(char* file) {
|
||||
const char* secret = file;
|
||||
unsigned long otp = generate_totp((unsigned char*)secret, strlen(secret));
|
||||
printf("%06lu\n", otp);
|
||||
gpgme_ctx_t ctx;
|
||||
gpgme_error_t err;
|
||||
gpgme_data_t in, out;
|
||||
size_t secret_len;
|
||||
|
||||
gpgme_check_version(NULL);
|
||||
err = gpgme_new(&ctx);
|
||||
if (err) {
|
||||
fprintf(stderr, "GPGMEを創作に失敗\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = gpgme_data_new_from_file(&in, file, 1);
|
||||
if (err) {
|
||||
fprintf(stderr, "GPGファイルを読込に失敗\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = gpgme_data_new(&out);
|
||||
if (err) {
|
||||
fprintf(stderr, "GPGデータを読込に失敗\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = gpgme_op_decrypt(ctx, in, out);
|
||||
if (err) {
|
||||
fprintf(stderr, "GPGを復号化に失敗\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char* secret = gpgme_data_release_and_get_mem(out, &secret_len);
|
||||
if (!secret) {
|
||||
fprintf(stderr, "GPGを受取に失敗\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t decoded_len;
|
||||
unsigned char* secret_decoded = extract_secret(secret, &decoded_len);
|
||||
if (!secret_decoded) {
|
||||
fprintf(stderr, "シークレットの抽出またはデコードに失敗しました\n");
|
||||
free(secret);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
time_t current_time = time(NULL);
|
||||
uint64_t counter = current_time / 30;
|
||||
uint32_t otp = generate_totp((const char*)secret_decoded, counter);
|
||||
printf("%06d\n", otp);
|
||||
|
||||
gpgme_data_release(in);
|
||||
gpgme_release(ctx);
|
||||
free(secret);
|
||||
free(secret_decoded);
|
||||
}
|
||||
|
|
読み込み中…
新しいイシューから参照