+# 1.1.0
+* TOTP対応
+* READMEファイルで使い方を詳しく説明する
+* zshキャプチャー
+* パスワード追加関数を安定化
+* パスワード表示とパスワードのコピー関数で、GNU Passで保存したパスワードの場合は改行を追加しない様に
+
# 1.0.0
* 最初リリース
NAME=sp
-VERSION=1.0.0
+VERSION=1.1.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
+FILES=main.c showpass.c yankpass.c addpass.c delpass.c listpass.c genpass.c initpass.c otppass.c
CFLAGS=-Wall -Wextra -g
-LDFLAGS=-lgpgme
+LDFLAGS=-lgpgme -lcrypto
all:
${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
+install-zsh:
+ cp sp-completion.zsh /usr/share/zsh/site-functions/_sp
+
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
## 初期設定
「gpg -k」でGPG鍵IDを確認して、「sp -i [GPG ID]」を実行して下さい。
+
+## 使い方
+### パスワードの作成
+#### 強いパスワードの場合
+```sh
+$ sp -g 64
+nSYGSF2lWGCUsqKCRB_~mZm+spaU<zvtt%um'01$tj4h,^nB6JqX#Cm$!U+s;c7:
+```
+又は
+```sh
+$ sp -g 64 secure
+nSYGSF2lWGCUsqKCRB_~mZm+spaU<zvtt%um'01$tj4h,^nB6JqX#Cm$!U+s;c7:
+```
+
+#### 弱いパスワードの場合
+```sh
+$ sp -g 12 risk
+2aKBwb858mzg
+```
+
+### パスワードの追加
+「suwako」というユーザー名の場合:
+```sh
+$ sp -a 076.moe/suwako
+パスワード:
+パスワード(確認用):
+パスワードを保存出来ました。
+```
+入力中は何も表示されないので、ご注意下さい。
+
+### パスワードの表示
+```sh
+$ sp -s 076.moe/suwako
+nSYGSF2lWGCUsqKCRB_~mZm+spaU<zvtt%um'01$tj4h,^nB6JqX#Cm$!U+s;c7:
+```
+表示せずコピーする場合は、「sp -y 076.moe/suwako」を使用して下さい。
+
+### パスワードの削除
+```sh
+$ sp -d 076.moe/suwako
+パスワード「076.moe/suwako」を本当に削除する事が宜しいでしょうか? (y/N): y
+パスワードを削除しました。
+```
+
+## TOTP(ワンタイムパスワード)
+### QRコードから
+QRコードをダウンロードし、zbarimgを使用して「QR-Code:」以降の部分をコピーし、spに追加して下さい。\
+`sp -a`を実行すると、「パスワード」を聞かれますが、TOTPの場合は「otpauth://」から始まる文字列をコピペして下さい。
+```sh
+$ zbarimg -q gitler.png
+QR-Code:otpauth://totp/Gitler%20%28gitler.moe%29:suwako?algorithm=SHA1&digits=6&issuer=Gitler%20%28gitler.moe%29&period=30&secret=〇〇
+
+$ sp -a gitler-otp
+パスワード:
+パスワード(確認用):
+パスワードを保存出来ました。
+
+$ sp -q gitler-otp
+123456
+```
tcsetattr(fileno(stdin), TCSANOW, &new);
// パスワードを読み込む
- fgets(pw, pwlen, stdin);
+ if (fgets(pw, pwlen, stdin) != NULL) {
+ // あれば、改行を取り消す
+ size_t len = strlen(pw);
+ if (len > 0 && pw[len - 1] == '\n') {
+ pw[len - 1] = '\0';
+ } else {
+ // 掃除
+ int c;
+ while ((c = getchar()) != '\n' && c != EOF);
+ }
+ }
// 端末設定をもとに戻す
tcsetattr(fileno(stdin), TCSANOW, &old);
-
- // 改行文字を取り消す
- pw[strcspn(pw, "\n")] = 0;
}
void addpass(char* file) {
char buffer[512];
ssize_t read_bytes;
+
while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer))) > 0) {
- fwrite(buffer, 1, read_bytes, gpgfile);
+ if (fwrite(buffer, 1, (size_t)read_bytes, gpgfile) != (size_t)read_bytes) {
+ perror("パスワードを書き込みに失敗");
+ free(gpgpath);
+ cleanup(ctx, key[0], in, out);
+ return;
+ }
}
// 掃除
#include "addpass.h"
#include "delpass.h"
#include "genpass.h"
-void otppass(char* file);
+#include "otppass.h"
void helpme();
const char* sofname = "sp";
-const char* version = "1.0.0";
+const char* version = "1.1.0";
void helpme() {
printf("076 sp - シンプルなパスワードマネージャー\n");
printf("%s -a <パスワード名> :パスワードを追加\n", sofname);
printf("%s -d <パスワード名> :パスワードを削除\n", sofname);
printf("%s -g <文字数> [risk|secure] :希望文字数でパスワードをランダムに作成する。risk=英数字のみ(不安)、secure=英数字+特別文字(デフォルト)を使用\n", sofname);
- /* printf("%s -o <パスワード名>\n :ワンタイムパスワード(TOTP)を表示。存在しなければ、創作する", sofname); */
+ printf("%s -o <パスワード名>\n :ワンタイムパスワード(TOTP)を表示。存在しなければ、創作する\n", sofname);
printf("%s -h :ヘルプを表示\n", sofname);
printf("%s -v :バージョンを表示\n", sofname);
}
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) printf("TODO: otp\n");
+ else if (argc == 3 && strcmp(argv[1], "-o") == 0) otppass(argv[2]);
else if (argc == 2 && strcmp(argv[1], "-v") == 0) printf("%s-%s\n", sofname, version);
else helpme();
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <openssl/hmac.h>
+
+unsigned long generate_totp(const unsigned char* secret, size_t keylen) {
+ unsigned long timestep = time(NULL) / 30;
+ unsigned char hmacres[20];
+
+ HMAC(EVP_sha1(), secret, keylen, (unsigned char*)×tep, sizeof(timestep), hmacres, NULL);
+
+ 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;
+}
+
+void otppass(char* file) {
+ const char* secret = file;
+ unsigned long otp = generate_totp((unsigned char*)secret, strlen(secret));
+ printf("%06lu\n", otp);
+}
--- /dev/null
+#ifndef OTPPASS_H
+#define OTPPASS_H
+
+void otppass(char* file);
+
+#endif
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <locale.h>
#include <gpgme.h>
gpgme_data_seek(out, 0, SEEK_SET);
char buffer[512];
ssize_t read_bytes;
+ bool islastnl = false;
+
while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer) - 1)) > 0) {
- buffer[read_bytes] = '\0';
- printf("%s", buffer);
+ fwrite(buffer, 1, read_bytes, stdout);
+ if (buffer[read_bytes - 1] == '\n') {
+ islastnl = true;
+ }
+ }
+
+ if (!islastnl) {
+ putchar('\n');
}
// 掃除
--- /dev/null
+# compdef sp
+
+_sp () {
+ local state
+ local -a options
+ local -a entries
+
+ _arguments -C \
+ '-i[GPGと使ってパスワードストレージを初期設定]: :_sp_complete_keys' \
+ '-s[パスワードを表示]: :_sp_complete_entries' \
+ '-y[パスワードを表示せずクリップボードにコピーする]: :_sp_complete_entries' \
+ '-l[パスワード一覧を表示]' \
+ '-a[パスワードを追加]: :_sp_complete_entries' \
+ '-d[パスワードを削除]: :_sp_complete_entries' \
+ '-g[希望文字数でパスワードをランダムに作成する]: :_sp_complete_entries' \
+ '-o[ワンタイムパスワード(TOTP)を表示]: :_sp_complete_entries' \
+ '-h[ヘルプを表示]' \
+ '-v[バージョンを表示]' \
+ '*:: :->subcmds'
+
+ case $state in
+ subcmds)
+ _message "no more arguments"
+ ;;
+ esac
+}
+
+_sp_cmd_show () {
+ _sp_complete_entries
+}
+
+_sp_complete_entries() {
+ _sp_complete_entries_helper -type f
+}
+
+_sp_complete_entries_helper () {
+ local IFS=$'\n'
+ local prefix="${SP_DIR:-$HOME/.local/share/sp}"
+ entries=("${(f)$(find -L "$prefix" \( -name .git -o -name .gpg-id \) -prune -o -type f -print 2>/dev/null | sed -e "s#${prefix}/##" -e 's#\.gpg$##')}")
+ _describe 'password entries' entries
+}
+
+_sp_complete_keys () {
+ local IFS=$'\n'
+ _values 'gpg keys' $(gpg2 --list-secret-keys --with-colons | cut -d : -f 10 | sort -u | sed '/^$/d')
+}
+
+compdef _sp sp
char buffer[512];
ssize_t read_bytes;
- while ((read_bytes = gpgme_data_read(out, buffer, 511)) > 0) {
+
+ while ((read_bytes = gpgme_data_read(out, buffer, sizeof(buffer))) > 0) {
+ if (buffer[read_bytes - 1] == '\n') {
+ read_bytes--;
+ }
fwrite(buffer, 1, read_bytes, pipe);
}
+
pclose(pipe);
// 45秒後、クリップボードから削除する
+ printf("パスワードをクリップボードに追加しました。\n45秒後はクリップボードから取り消されます。\n");
sleep(45);
system("echo -n | xclip -selection clipboard");